[Sb refactor] Add the default network's connectivity to the view model

Part of the criteria of whether or not to show the RAT indicator is
whether or not `mobile` is a connected transport. The way the old
pipeline does this is by keeping track of the default network
capabilities (via `ConnectivityManager`) and letting those be known by
all mobile connections.

The equivalent in the new pipeline is to expose the default network
capabilities via the MobileIconsInteractor, and pass it down to each
individual icon interactor.

Finally, the reason this is sensible to do in the new pipeline is that
we can track when the data subscription changes and thus we can avoid
icon flickering (see following CL)

Test: MobileIconViewModelTest
Test: MobileIconInteractorTest
Test: MobileIconsInteractorTest
Bug: 264683083
Bug: 238425913
Change-Id: Ib3a4f14ba3cb403f342090b7394d271309a3a7f5
This commit is contained in:
Evan Laird
2023-01-10 15:38:56 -05:00
parent 1b65eb52b1
commit 5ceb454c8e
8 changed files with 118 additions and 12 deletions

View File

@@ -21,6 +21,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -41,12 +42,29 @@ interface MobileIconInteractor {
/** The current mobile data activity */
val activity: Flow<DataActivityModel>
/**
* This bit is meant to be `true` if and only if the default network capabilities (see
* [android.net.ConnectivityManager.registerDefaultNetworkCallback]) result in a network that
* has the [android.net.NetworkCapabilities.TRANSPORT_CELLULAR] represented.
*
* Note that this differs from [isDataConnected], which is tracked by telephony and has to do
* with the state of using this mobile connection for data as opposed to just voice. It is
* possible for a mobile subscription to be connected but not be in a connected data state, and
* thus we wouldn't want to show the network type icon.
*/
val isConnected: Flow<Boolean>
/**
* True when telephony tells us that the data state is CONNECTED. See
* [android.telephony.TelephonyCallback.DataConnectionStateListener] for more details. We
* consider this connection to be serving data, and thus want to show a network type icon, when
* data is connected. Other data connection states would typically cause us not to show the icon
*/
val isDataConnected: StateFlow<Boolean>
/** Only true if mobile is the default transport but is not validated, otherwise false */
val isDefaultConnectionFailed: StateFlow<Boolean>
/** True when telephony tells us that the data state is CONNECTED */
val isDataConnected: StateFlow<Boolean>
/** True if we consider this connection to be in service, i.e. can make calls */
val isInService: StateFlow<Boolean>
@@ -100,6 +118,7 @@ class MobileIconInteractorImpl(
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
defaultMobileConnectivity: StateFlow<MobileConnectivityModel>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
override val isDefaultConnectionFailed: StateFlow<Boolean>,
@@ -111,6 +130,8 @@ class MobileIconInteractorImpl(
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
override val isConnected: Flow<Boolean> = defaultMobileConnectivity.mapLatest { it.isConnected }
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled

View File

@@ -23,6 +23,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -62,6 +63,14 @@ interface MobileIconsInteractor {
/** True if the CDMA level should be preferred over the primary level. */
val alwaysUseCdmaLevel: StateFlow<Boolean>
/**
* The connectivity of the default mobile network. Note that this can differ from what is
* reported from [MobileConnectionsRepository] in some cases. E.g., when the active subscription
* changes but the groupUuid remains the same, we keep the old validation information for 2
* seconds to avoid icon flickering.
*/
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
@@ -154,6 +163,9 @@ constructor(
}
}
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
mobileConnectionsRepo.defaultMobileNetworkConnectivity
/**
* Mapping from network type to [MobileIconGroup] using the config generated for the default
* subscription Id. This mapping is the same for every subscription.
@@ -207,6 +219,7 @@ constructor(
activeDataConnectionHasDataEnabled,
alwaysShowDataRatIcon,
alwaysUseCdmaLevel,
defaultMobileNetworkConnectivity,
defaultMobileIconMapping,
defaultMobileIconGroup,
isDefaultConnectionFailed,

View File

@@ -102,24 +102,29 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
private val showNetworkTypeIcon: Flow<Boolean> =
combine(
iconInteractor.isDataConnected,
iconInteractor.isDataEnabled,
iconInteractor.isDefaultConnectionFailed,
iconInteractor.alwaysShowDataRatIcon,
iconInteractor.isConnected,
) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
}
override val networkTypeIcon: Flow<Icon?> =
combine(
iconInteractor.networkTypeIconGroup,
iconInteractor.isDataConnected,
iconInteractor.isDataEnabled,
iconInteractor.isDefaultConnectionFailed,
iconInteractor.alwaysShowDataRatIcon,
) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
showNetworkTypeIcon,
) { networkTypeIconGroup, shouldShow ->
val desc =
if (networkTypeIconGroup.dataContentDescription != 0)
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
else null
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
return@combine when {
alwaysShow -> icon
!dataConnected -> null
!dataEnabled -> null
failedConnection -> null
!shouldShow -> null
else -> icon
}
}

View File

@@ -40,6 +40,8 @@ class FakeMobileIconInteractor(
)
)
override val isConnected = MutableStateFlow(true)
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
override val networkTypeIconGroup = _iconGroup

View File

@@ -23,6 +23,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import kotlinx.coroutines.flow.MutableStateFlow
@@ -60,6 +61,8 @@ class FakeMobileIconsInteractor(
override val alwaysUseCdmaLevel = MutableStateFlow(false)
override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
override val defaultMobileIconMapping = _defaultMobileIconMapping

View File

@@ -61,6 +61,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
mobileIconsInteractor.alwaysShowDataRatIcon,
mobileIconsInteractor.alwaysUseCdmaLevel,
mobileIconsInteractor.defaultMobileNetworkConnectivity,
mobileIconsInteractor.defaultMobileIconMapping,
mobileIconsInteractor.defaultMobileIconGroup,
mobileIconsInteractor.isDefaultConnectionFailed,

View File

@@ -327,6 +327,32 @@ class MobileIconsInteractorTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `default mobile connectivity - uses repo value`() =
testScope.runTest {
var latest: MobileConnectivityModel? = null
val job =
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
var expected = MobileConnectivityModel(isConnected = true, isValidated = true)
connectionsRepository.setMobileConnectivity(expected)
assertThat(latest).isEqualTo(expected)
expected = MobileConnectivityModel(isConnected = false, isValidated = true)
connectionsRepository.setMobileConnectivity(expected)
assertThat(latest).isEqualTo(expected)
expected = MobileConnectivityModel(isConnected = true, isValidated = false)
connectionsRepository.setMobileConnectivity(expected)
assertThat(latest).isEqualTo(expected)
expected = MobileConnectivityModel(isConnected = false, isValidated = false)
connectionsRepository.setMobileConnectivity(expected)
assertThat(latest).isEqualTo(expected)
job.cancel()
}
companion object {
private val tableLogBuffer =
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())

View File

@@ -273,6 +273,41 @@ class MobileIconViewModelTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `network type - alwaysShow - shown when not connected`() =
testScope.runTest {
interactor.setIconGroup(THREE_G)
interactor.isConnected.value = false
interactor.alwaysShowDataRatIcon.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
val expected =
Icon.Resource(
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
assertThat(latest).isEqualTo(expected)
job.cancel()
}
@Test
fun `network type - not shown when not connected`() =
testScope.runTest {
interactor.setIconGroup(THREE_G)
interactor.isDataConnected.value = true
interactor.isConnected.value = false
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
assertThat(latest).isNull()
job.cancel()
}
@Test
fun roaming() =
testScope.runTest {