Merge "[sat] Table log buffers" into main
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.kt
new file mode 100644
index 0000000..a40d510
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.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.pipeline.dagger
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class DeviceBasedSatelliteTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index a81bfa4..4850049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -239,6 +239,13 @@
             return factory.create("VerboseDeviceBasedSatelliteInputLog", 200)
         }
 
+        @Provides
+        @SysUISingleton
+        @DeviceBasedSatelliteTableLog
+        fun provideDeviceBasedSatelliteTableLog(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("DeviceBasedSatelliteTableLog", 200)
+        }
+
         const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON =
             "FirstMobileSubShowingNetworkTypeIcon"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index cc4d568..26553e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -385,7 +385,15 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
-        mobileConnectionsRepo.deviceServiceState.map { it?.isEmergencyOnly ?: false }
+        mobileConnectionsRepo.deviceServiceState
+            .map { it?.isEmergencyOnly ?: false }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = LOGGING_PREFIX,
+                columnName = "deviceEmergencyOnly",
+                initialValue = false,
+            )
 
     /** Vends out new [MobileIconInteractor] for a particular subId */
     override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
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 03f88c7..f1a444f 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
@@ -21,7 +21,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteTableLog
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -33,6 +36,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -47,6 +51,7 @@
     wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
     @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
+    @DeviceBasedSatelliteTableLog private val tableLog: TableLogBuffer,
 ) {
     /** Must be observed by any UI showing Satellite iconography */
     val isSatelliteAllowed =
@@ -55,6 +60,13 @@
             } else {
                 flowOf(false)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_ALLOWED,
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     /** See [SatelliteConnectionState] for relevant states */
@@ -65,6 +77,12 @@
 
                 flowOf(SatelliteConnectionState.Off)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                initialValue = SatelliteConnectionState.Off,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), SatelliteConnectionState.Off)
 
     /** 0-4 description of the connection strength */
@@ -74,6 +92,13 @@
             } else {
                 flowOf(0)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_LEVEL,
+                initialValue = 0,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
     val isSatelliteProvisioned = repo.isSatelliteProvisioned
@@ -82,19 +107,27 @@
         wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
 
     private val allConnectionsOos =
-        iconsInteractor.icons.aggregateOver(
-            selector = { intr ->
-                combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
-                    isInService,
-                    isEmergencyOnly,
-                    isNtn ->
-                    !isInService && !isEmergencyOnly && !isNtn
-                }
-            },
-            defaultValue = true, // no connections == everything is OOS
-        ) { isOosAndNotEmergencyAndNotSatellite ->
-            isOosAndNotEmergencyAndNotSatellite.all { it }
-        }
+        iconsInteractor.icons
+            .aggregateOver(
+                selector = { intr ->
+                    combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
+                        isInService,
+                        isEmergencyOnly,
+                        isNtn ->
+                        !isInService && !isEmergencyOnly && !isNtn
+                    }
+                },
+                defaultValue = true, // no connections == everything is OOS
+            ) { isOosAndNotEmergencyAndNotSatellite ->
+                isOosAndNotEmergencyAndNotSatellite.all { it }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_ALL_OOS,
+                initialValue = true,
+            )
 
     /** When all connections are considered OOS, satellite connectivity is potentially valid */
     val areAllConnectionsOutOfService =
@@ -122,10 +155,24 @@
             } else {
                 flowOf(false)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_FULL_OOS,
+                initialValue = true,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
 
     companion object {
         const val TAG = "DeviceBasedSatelliteInteractor"
+
+        const val COL_LEVEL = "level"
+        const val COL_ALL_OOS = "allConnsOOS"
+        const val COL_ALLOWED = "allowed"
+        // Going to try to optimize for not using too much width on the table here. This information
+        // can be ascertained by checking for the device emergency only in the mobile logs as well
+        const val COL_FULL_OOS = "allOosAndNoEmer"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
index bfe2941..905ed730 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
@@ -26,8 +26,10 @@
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
 
-enum class SatelliteConnectionState {
+enum class SatelliteConnectionState : Diffable<SatelliteConnectionState> {
     // State is unknown or undefined
     Unknown,
     // Radio is off
@@ -37,7 +39,15 @@
     // Radio is connected, aka satellite is available for use
     Connected;
 
+    override fun logDiffs(prevVal: SatelliteConnectionState, row: TableRowLogger) {
+        if (prevVal != this) {
+            row.logChange(COL_CONNECTION_STATE, name)
+        }
+    }
+
     companion object {
+        const val COL_CONNECTION_STATE = "connState"
+
         // TODO(b/316635648): validate these states. We don't need the level of granularity that
         //  SatelliteManager gives us.
         fun fromModemState(@SatelliteManager.SatelliteModemState modemState: Int) =
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 48278d4..199b5b67 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
@@ -22,9 +22,12 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteTableLog
 import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
@@ -71,22 +74,34 @@
     @Application scope: CoroutineScope,
     airplaneModeRepository: AirplaneModeRepository,
     @DeviceBasedSatelliteInputLog logBuffer: LogBuffer,
+    @DeviceBasedSatelliteTableLog tableLog: TableLogBuffer,
 ) : DeviceBasedSatelliteViewModel {
     private val shouldShowIcon: Flow<Boolean> =
-        interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
-            if (!allOos) {
-                flowOf(false)
-            } else {
-                combine(
-                    interactor.isSatelliteAllowed,
-                    interactor.isSatelliteProvisioned,
-                    interactor.isWifiActive,
-                    airplaneModeRepository.isAirplaneMode
-                ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
-                    isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
+        interactor.areAllConnectionsOutOfService
+            .flatMapLatest { allOos ->
+                if (!allOos) {
+                    flowOf(false)
+                } else {
+                    combine(
+                        interactor.isSatelliteAllowed,
+                        interactor.isSatelliteProvisioned,
+                        interactor.isWifiActive,
+                        airplaneModeRepository.isAirplaneMode
+                    ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
+                        isSatelliteAllowed &&
+                            isSatelliteProvisioned &&
+                            !isWifiActive &&
+                            !isAirplaneMode
+                    }
                 }
             }
-        }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "vm",
+                columnName = COL_VISIBLE_CONDITION,
+                initialValue = false,
+            )
 
     // This adds a 10 seconds delay before showing the icon
     private val shouldActuallyShowIcon: StateFlow<Boolean> =
@@ -106,6 +121,13 @@
                     flowOf(false)
                 }
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "vm",
+                columnName = COL_VISIBLE,
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val icon: StateFlow<Icon?> =
@@ -163,5 +185,8 @@
     companion object {
         private const val TAG = "DeviceBasedSatelliteViewModel"
         private val DELAY_DURATION = 10.seconds
+
+        const val COL_VISIBLE_CONDITION = "visCondition"
+        const val COL_VISIBLE = "visible"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index cd0390e..dbb77d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -70,6 +70,7 @@
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
     }
 
@@ -113,6 +114,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.isSatelliteAllowed)
@@ -161,6 +163,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.connectionState)
@@ -217,6 +220,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.signalStrength)
@@ -535,6 +539,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 64b07fc..c3cc33f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlin.time.Duration.Companion.seconds
@@ -44,6 +43,7 @@
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -73,6 +73,7 @@
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
 
         underTest =
@@ -82,6 +83,7 @@
                 testScope.backgroundScope,
                 airplaneModeRepository,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
     }