From 5ceb454c8e8465ddd6df1e046f46719b3bb1702d Mon Sep 17 00:00:00 2001 From: Evan Laird Date: Tue, 10 Jan 2023 15:38:56 -0500 Subject: [PATCH] [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 --- .../domain/interactor/MobileIconInteractor.kt | 27 ++++++++++++-- .../interactor/MobileIconsInteractor.kt | 13 +++++++ .../ui/viewmodel/MobileIconViewModel.kt | 23 +++++++----- .../interactor/FakeMobileIconInteractor.kt | 2 ++ .../interactor/FakeMobileIconsInteractor.kt | 3 ++ .../interactor/MobileIconInteractorTest.kt | 1 + .../interactor/MobileIconsInteractorTest.kt | 26 ++++++++++++++ .../ui/viewmodel/MobileIconViewModelTest.kt | 35 +++++++++++++++++++ 8 files changed, 118 insertions(+), 12 deletions(-) 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 003df2482c6e4..f85a1213e9287 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 @@ -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 + /** + * 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 + + /** + * 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 + /** Only true if mobile is the default transport but is not validated, otherwise false */ val isDefaultConnectionFailed: StateFlow - /** True when telephony tells us that the data state is CONNECTED */ - val isDataConnected: StateFlow - /** True if we consider this connection to be in service, i.e. can make calls */ val isInService: StateFlow @@ -100,6 +118,7 @@ class MobileIconInteractorImpl( defaultSubscriptionHasDataEnabled: StateFlow, override val alwaysShowDataRatIcon: StateFlow, override val alwaysUseCdmaLevel: StateFlow, + defaultMobileConnectivity: StateFlow, defaultMobileIconMapping: StateFlow>, defaultMobileIconGroup: StateFlow, override val isDefaultConnectionFailed: StateFlow, @@ -111,6 +130,8 @@ class MobileIconInteractorImpl( override val activity = connectionInfo.mapLatest { it.dataActivityDirection } + override val isConnected: Flow = defaultMobileConnectivity.mapLatest { it.isConnected } + override val isDataEnabled: StateFlow = connectionRepository.dataEnabled override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled 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 83da1dd067806..96196e72b1031 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 @@ -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 + /** + * 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 + /** The icon mapping from network type to [MobileIconGroup] for the default subscription */ val defaultMobileIconMapping: StateFlow> /** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */ @@ -154,6 +163,9 @@ constructor( } } + override val defaultMobileNetworkConnectivity: StateFlow = + 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, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index a2117c7df188e..5e9356163e6e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -102,24 +102,29 @@ constructor( .stateIn(scope, SharingStarted.WhileSubscribed(), initial) } + private val showNetworkTypeIcon: Flow = + 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 = 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 } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index a29146b016680..7aeaa48165aa6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -40,6 +40,8 @@ class FakeMobileIconInteractor( ) ) + override val isConnected = MutableStateFlow(true) + private val _iconGroup = MutableStateFlow(TelephonyIcons.THREE_G) override val networkTypeIconGroup = _iconGroup diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt index 1c0064610c52f..e57ad768ec8a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -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 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 e6be7f15235b3..a774ffeb997a2 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 @@ -61,6 +61,7 @@ class MobileIconInteractorTest : SysuiTestCase() { mobileIconsInteractor.activeDataConnectionHasDataEnabled, mobileIconsInteractor.alwaysShowDataRatIcon, mobileIconsInteractor.alwaysUseCdmaLevel, + mobileIconsInteractor.defaultMobileNetworkConnectivity, mobileIconsInteractor.defaultMobileIconMapping, mobileIconsInteractor.defaultMobileIconGroup, mobileIconsInteractor.isDefaultConnectionFailed, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 721d49a13834e..9e8ae6db449a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -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()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 2a8d42ff69975..a24e29aebc1ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -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 {