summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Olivier St-Onge <ostonge@google.com> 2024-03-06 13:17:30 -0500
committer Olivier St-Onge <ostonge@google.com> 2024-03-13 15:58:11 -0400
commit2fcb2e5cf14e9880dd74cb98c1b29ca940e83806 (patch)
treeccc65def616fba63697d463be117781549035a2b
parent0ddacc17d72c0fbc1040c30144bd15c41a2eff64 (diff)
Add hysteresis to satellite icon when losing connection.
The duration is taken from the carrier config value KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT. Flag: none Test: MobileIconInteractorTest Fixes: 326441908 Change-Id: I0bacafe50e13d44f580ffef26c7547ae8eaed0bd
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt2
10 files changed, 136 insertions, 8 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
index f4e3eab8593d..3b2930f78d19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.flow.MutableStateFlow
@@ -42,10 +43,11 @@ import kotlinx.coroutines.flow.asStateFlow
* using the default config for logging purposes.
*
* NOTE to add new keys to be tracked:
- * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig]
- * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config]
- * 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly
- * updated when a new carrier config comes down
+ * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig] or [IntCarrierConfig]
+ * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config] or
+ * [IntCarrierConfig.config]
+ * 3. Add the new wrapped public flow to the list of tracked configs, so they are properly updated
+ * when a new carrier config comes down
*/
class SystemUiCarrierConfig
internal constructor(
@@ -66,10 +68,16 @@ internal constructor(
/** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
+ private val satelliteHysteresisSeconds =
+ IntCarrierConfig(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, defaultConfig)
+ /** Flow tracking the [KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT] config */
+ val satelliteConnectionHysteresisSeconds: StateFlow<Int> = satelliteHysteresisSeconds.config
+
private val trackedConfigs =
listOf(
inflateSignalStrength,
showOperatorName,
+ satelliteHysteresisSeconds,
)
/** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
@@ -90,15 +98,19 @@ internal constructor(
override fun toString(): String = trackedConfigs.joinToString { it.toString() }
}
+interface CarrierConfig {
+ fun update(config: PersistableBundle)
+}
+
/** Extracts [key] from the carrier config, and stores it in a flow */
private class BooleanCarrierConfig(
val key: String,
defaultConfig: PersistableBundle,
-) {
+) : CarrierConfig {
private val _configValue = MutableStateFlow(defaultConfig.getBoolean(key))
val config = _configValue.asStateFlow()
- fun update(config: PersistableBundle) {
+ override fun update(config: PersistableBundle) {
_configValue.value = config.getBoolean(key)
}
@@ -106,3 +118,20 @@ private class BooleanCarrierConfig(
return "$key=${config.value}"
}
}
+
+/** Extracts [key] from the carrier config, and stores it in a flow */
+private class IntCarrierConfig(
+ val key: String,
+ defaultConfig: PersistableBundle,
+) : CarrierConfig {
+ private val _configValue = MutableStateFlow(defaultConfig.getInt(key))
+ val config = _configValue.asStateFlow()
+
+ override fun update(config: PersistableBundle) {
+ _configValue.value = config.getInt(key)
+ }
+
+ override fun toString(): String {
+ return "$key=${config.value}"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 8f3b0e788dae..8f00b43e79f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -141,6 +141,9 @@ interface MobileConnectionRepository {
*/
val hasPrioritizedNetworkCapabilities: StateFlow<Boolean>
+ /** Duration in seconds of the hysteresis to use when losing satellite connection. */
+ val satelliteConnectionHysteresisSeconds: StateFlow<Int>
+
/**
* True if this connection is in emergency callback mode.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 3cb138bd75a9..af34a57c7242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -206,6 +206,8 @@ class DemoMobileConnectionRepository(
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
+ override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0)
+
override suspend fun isInEcmMode(): Boolean = false
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index f8858c5037ab..2bc3bcbc8bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -186,6 +186,9 @@ class CarrierMergedConnectionRepository(
*/
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false).asStateFlow()
+ /** Non-applicable to carrier merged connections. */
+ override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0).asStateFlow()
+
override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
override suspend fun isInEcmMode(): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index a1241965de7a..60b8599ecabd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -334,6 +334,15 @@ class FullMobileConnectionRepository(
activeRepo.value.hasPrioritizedNetworkCapabilities.value,
)
+ override val satelliteConnectionHysteresisSeconds =
+ activeRepo
+ .flatMapLatest { it.satelliteConnectionHysteresisSeconds }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ activeRepo.value.satelliteConnectionHysteresisSeconds.value
+ )
+
override suspend fun isInEcmMode(): Boolean = activeRepo.value.isInEcmMode()
class Factory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 77fd6bef8a33..f01ac0e0a677 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -442,6 +442,9 @@ class MobileConnectionRepositoryImpl(
.flowOn(bgDispatcher)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val satelliteConnectionHysteresisSeconds: StateFlow<Int> =
+ systemUiCarrierConfig.satelliteConnectionHysteresisSeconds
+
class Factory
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index adcf736bba01..9d194cfca350 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -35,18 +35,25 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIc
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.util.kotlin.pairwiseBy
+import kotlin.time.DurationUnit
+import kotlin.time.toDuration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
interface MobileIconInteractor {
/** The table log created for this connection */
@@ -262,6 +269,43 @@ class MobileIconInteractorImpl(
MutableStateFlow(false).asStateFlow()
}
+ private val hysteresisActive = MutableStateFlow(false)
+
+ private val isNonTerrestrialWithHysteresis: StateFlow<Boolean> =
+ combine(isNonTerrestrial, hysteresisActive) { isNonTerrestrial, hysteresisActive ->
+ if (hysteresisActive) {
+ true
+ } else {
+ isNonTerrestrial
+ }
+ }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isNonTerrestrialWithHysteresis",
+ columnPrefix = "",
+ initialValue = Flags.carrierEnabledSatelliteFlag(),
+ )
+ .stateIn(scope, SharingStarted.Eagerly, Flags.carrierEnabledSatelliteFlag())
+
+ private val lostSatelliteConnection =
+ isNonTerrestrial.pairwiseBy { old, new -> hysteresisActive.value = old && !new }
+
+ init {
+ scope.launch { lostSatelliteConnection.collect() }
+ scope.launch {
+ hysteresisActive.collectLatest {
+ if (it) {
+ delay(
+ connectionRepository.satelliteConnectionHysteresisSeconds.value.toDuration(
+ DurationUnit.SECONDS
+ )
+ )
+ hysteresisActive.value = false
+ }
+ }
+ }
+ }
+
override val isRoaming: StateFlow<Boolean> =
combine(
connectionRepository.carrierNetworkChangeActive,
@@ -360,7 +404,7 @@ class MobileIconInteractorImpl(
showExclamationMark.value,
carrierNetworkChangeActive.value,
)
- isNonTerrestrial
+ isNonTerrestrialWithHysteresis
.flatMapLatest { ntn ->
if (ntn) {
satelliteIcon
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 1fb6e2c7a232..c13e830afac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_EMERGENCY
@@ -679,6 +680,9 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
telephonyManager: TelephonyManager,
): MobileConnectionRepositoryImpl {
whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
+ val systemUiCarrierConfigMock: SystemUiCarrierConfig = mock()
+ whenever(systemUiCarrierConfigMock.satelliteConnectionHysteresisSeconds)
+ .thenReturn(MutableStateFlow(0))
val realRepo =
MobileConnectionRepositoryImpl(
@@ -689,7 +693,7 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
SEP,
connectivityManager,
telephonyManager,
- systemUiCarrierConfig = mock(),
+ systemUiCarrierConfig = systemUiCarrierConfigMock,
fakeBroadcastDispatcher,
mobileMappingsProxy = mock(),
testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 49953a1176fd..c49fcf88ecaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -43,12 +43,15 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.DurationUnit
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -675,6 +678,32 @@ class MobileIconInteractorTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
}
+ @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @Test
+ fun satBasedIcon_hasHysteresisWhenDisabled() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.signalLevelIcon)
+
+ val hysteresisDuration = 5.seconds
+ connectionRepository.satelliteConnectionHysteresisSeconds.value =
+ hysteresisDuration.toInt(DurationUnit.SECONDS)
+
+ connectionRepository.isNonTerrestrial.value = true
+
+ assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
+
+ // Disable satellite
+ connectionRepository.isNonTerrestrial.value = false
+
+ // Satellite icon should still be visible
+ assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
+
+ // Wait for the icon to change
+ advanceTimeBy(hysteresisDuration)
+
+ assertThat(latest).isInstanceOf(SignalIconModel.Cellular::class.java)
+ }
+
private fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
) =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 32d572ef9dee..2d5a3612ff6a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -63,6 +63,8 @@ class FakeMobileConnectionRepository(
override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
+ override val satelliteConnectionHysteresisSeconds = MutableStateFlow(0)
+
private var isInEcmMode: Boolean = false
override suspend fun isInEcmMode(): Boolean = isInEcmMode