Merge changes I05fa65f3,Ie576d9ce into udc-dev

* changes:
  [SB Refactor] Use carrierMergedSubId for the mobile default calculation.
  [Status bar] Take underlying networks into account for wifi.
This commit is contained in:
Caitlin Shkuratov
2023-04-06 14:13:24 +00:00
committed by Android (Google) Code Review
12 changed files with 851 additions and 84 deletions

View File

@@ -60,6 +60,13 @@ interface MobileConnectionsRepository {
*/ */
val mobileIsDefault: StateFlow<Boolean> val mobileIsDefault: StateFlow<Boolean>
/**
* True if the device currently has a carrier merged connection.
*
* See [CarrierMergedConnectionRepository] for more info.
*/
val hasCarrierMergedConnection: Flow<Boolean>
/** True if the default network connection is validated and false otherwise. */ /** True if the default network connection is validated and false otherwise. */
val defaultConnectionIsValidated: StateFlow<Boolean> val defaultConnectionIsValidated: StateFlow<Boolean>

View File

@@ -159,6 +159,15 @@ constructor(
.flatMapLatest { it.mobileIsDefault } .flatMapLatest { it.mobileIsDefault }
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.mobileIsDefault.value) .stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.mobileIsDefault.value)
override val hasCarrierMergedConnection: StateFlow<Boolean> =
activeRepo
.flatMapLatest { it.hasCarrierMergedConnection }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
realRepository.hasCarrierMergedConnection.value,
)
override val defaultConnectionIsValidated: StateFlow<Boolean> = override val defaultConnectionIsValidated: StateFlow<Boolean> =
activeRepo activeRepo
.flatMapLatest { it.defaultConnectionIsValidated } .flatMapLatest { it.defaultConnectionIsValidated }

View File

@@ -159,6 +159,9 @@ constructor(
// TODO(b/261029387): not yet supported // TODO(b/261029387): not yet supported
override val mobileIsDefault: StateFlow<Boolean> = MutableStateFlow(true) override val mobileIsDefault: StateFlow<Boolean> = MutableStateFlow(true)
// TODO(b/261029387): not yet supported
override val hasCarrierMergedConnection = MutableStateFlow(false)
// TODO(b/261029387): not yet supported // TODO(b/261029387): not yet supported
override val defaultConnectionIsValidated: StateFlow<Boolean> = MutableStateFlow(true) override val defaultConnectionIsValidated: StateFlow<Boolean> = MutableStateFlow(true)

View File

@@ -59,6 +59,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@@ -257,18 +258,32 @@ constructor(
override val mobileIsDefault: StateFlow<Boolean> = override val mobileIsDefault: StateFlow<Boolean> =
connectivityRepository.defaultConnections connectivityRepository.defaultConnections
// Because carrier merged networks are displayed as mobile networks, they're .map { it.mobile.isDefault }
// part of the `isDefault` calculation. See b/272586234.
.map { it.mobile.isDefault || it.carrierMerged.isDefault }
.distinctUntilChanged() .distinctUntilChanged()
.logDiffsForTable( .logDiffsForTable(
tableLogger, tableLogger,
columnPrefix = "", columnPrefix = LOGGING_PREFIX,
columnName = "mobileIsDefault", columnName = "mobileIsDefault",
initialValue = false, initialValue = false,
) )
.stateIn(scope, SharingStarted.WhileSubscribed(), false) .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val hasCarrierMergedConnection: StateFlow<Boolean> =
combine(
connectivityRepository.defaultConnections,
carrierMergedSubId,
) { defaultConnections, carrierMergedSubId ->
defaultConnections.carrierMerged.isDefault || carrierMergedSubId != null
}
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
columnPrefix = LOGGING_PREFIX,
columnName = "hasCarrierMergedConnection",
initialValue = false,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val defaultConnectionIsValidated: StateFlow<Boolean> = override val defaultConnectionIsValidated: StateFlow<Boolean> =
connectivityRepository.defaultConnections connectivityRepository.defaultConnections
.map { it.isValidated } .map { it.isValidated }

View File

@@ -113,7 +113,22 @@ constructor(
private val context: Context, private val context: Context,
) : MobileIconsInteractor { ) : MobileIconsInteractor {
override val mobileIsDefault = mobileConnectionsRepo.mobileIsDefault override val mobileIsDefault =
combine(
mobileConnectionsRepo.mobileIsDefault,
mobileConnectionsRepo.hasCarrierMergedConnection,
) { mobileIsDefault, hasCarrierMergedConnection ->
// Because carrier merged networks are displayed as mobile networks, they're part of
// the `isDefault` calculation. See b/272586234.
mobileIsDefault || hasCarrierMergedConnection
}
.logDiffsForTable(
tableLogger,
LOGGING_PREFIX,
columnName = "mobileIsDefault",
initialValue = false,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> = override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
mobileConnectionsRepo.activeMobileDataRepository mobileConnectionsRepo.activeMobileDataRepository

View File

@@ -44,11 +44,11 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnecti
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Ethernet import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Ethernet
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Mobile import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Mobile
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService
import java.io.PrintWriter import java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -68,12 +68,12 @@ interface ConnectivityRepository {
val defaultConnections: StateFlow<DefaultConnectionModel> val defaultConnections: StateFlow<DefaultConnectionModel>
} }
@OptIn(ExperimentalCoroutinesApi::class) @SuppressLint("MissingPermission")
@SysUISingleton @SysUISingleton
class ConnectivityRepositoryImpl class ConnectivityRepositoryImpl
@Inject @Inject
constructor( constructor(
connectivityManager: ConnectivityManager, private val connectivityManager: ConnectivityManager,
private val connectivitySlots: ConnectivitySlots, private val connectivitySlots: ConnectivitySlots,
context: Context, context: Context,
dumpManager: DumpManager, dumpManager: DumpManager,
@@ -144,15 +144,14 @@ constructor(
) { ) {
logger.logOnDefaultCapabilitiesChanged(network, networkCapabilities) logger.logOnDefaultCapabilitiesChanged(network, networkCapabilities)
val wifiInfo =
networkCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
val isWifiDefault = val isWifiDefault =
networkCapabilities.hasTransport(TRANSPORT_WIFI) || networkCapabilities.hasTransport(TRANSPORT_WIFI) || wifiInfo != null
networkCapabilities.getMainOrUnderlyingWifiInfo() != null
val isMobileDefault = val isMobileDefault =
networkCapabilities.hasTransport(TRANSPORT_CELLULAR) networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
val isCarrierMergedDefault = val isCarrierMergedDefault = wifiInfo?.isCarrierMerged == true
networkCapabilities
.getMainOrUnderlyingWifiInfo()
?.isCarrierMerged == true
val isEthernetDefault = val isEthernetDefault =
networkCapabilities.hasTransport(TRANSPORT_ETHERNET) networkCapabilities.hasTransport(TRANSPORT_ETHERNET)
@@ -209,7 +208,32 @@ constructor(
* always use [WifiInfo] if it's available, so we need to check the underlying transport * always use [WifiInfo] if it's available, so we need to check the underlying transport
* info. * info.
*/ */
fun NetworkCapabilities.getMainOrUnderlyingWifiInfo(): WifiInfo? { fun NetworkCapabilities.getMainOrUnderlyingWifiInfo(
connectivityManager: ConnectivityManager,
): WifiInfo? {
val mainWifiInfo = this.getMainWifiInfo()
if (mainWifiInfo != null) {
return mainWifiInfo
}
// Only CELLULAR networks may have underlying wifi information that's relevant to SysUI,
// so skip the underlying network check if it's not CELLULAR.
if (!this.hasTransport(TRANSPORT_CELLULAR)) {
return mainWifiInfo
}
// Some connections, like VPN connections, may have underlying networks that are
// eventually traced to a wifi or carrier merged connection. So, check those underlying
// networks for possible wifi information as well. See b/225902574.
return this.underlyingNetworks?.firstNotNullOfOrNull { underlyingNetwork ->
connectivityManager.getNetworkCapabilities(underlyingNetwork)?.getMainWifiInfo()
}
}
/**
* Checks the network capabilities for wifi info, but does *not* check the underlying
* networks. See [getMainOrUnderlyingWifiInfo].
*/
private fun NetworkCapabilities.getMainWifiInfo(): WifiInfo? {
// Wifi info can either come from a WIFI Transport, or from a CELLULAR transport for // Wifi info can either come from a WIFI Transport, or from a CELLULAR transport for
// virtual networks like VCN. // virtual networks like VCN.
val canHaveWifiInfo = val canHaveWifiInfo =

View File

@@ -138,7 +138,8 @@ constructor(
wifiNetworkChangeEvents.tryEmit(Unit) wifiNetworkChangeEvents.tryEmit(Unit)
val wifiInfo = networkCapabilities.getMainOrUnderlyingWifiInfo() val wifiInfo =
networkCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
if (wifiInfo?.isPrimary == true) { if (wifiInfo?.isPrimary == true) {
val wifiNetworkModel = val wifiNetworkModel =
createWifiNetworkModel( createWifiNetworkModel(

View File

@@ -67,6 +67,8 @@ class FakeMobileConnectionsRepository(
override val mobileIsDefault = MutableStateFlow(false) override val mobileIsDefault = MutableStateFlow(false)
override val hasCarrierMergedConnection = MutableStateFlow(false)
override val defaultConnectionIsValidated = MutableStateFlow(false) override val defaultConnectionIsValidated = MutableStateFlow(false)
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>() private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()

View File

@@ -68,6 +68,7 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import org.junit.After import org.junit.After
import org.junit.Assert.assertThrows import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@@ -766,28 +767,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_carrierMergedViaWifi_isDefault() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
assertThat(latest).isTrue()
job.cancel()
}
@Test @Test
fun mobileIsDefault_carrierMergedViaMobile_isDefault() = fun mobileIsDefault_carrierMergedViaMobile_isDefault() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
@@ -809,49 +788,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_carrierMergedViaWifiWithVcnTransport_isDefault() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun mobileIsDefault_carrierMergedViaMobileWithVcnTransport_isDefault() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
assertThat(latest).isTrue()
job.cancel()
}
@Test @Test
fun mobileIsDefault_wifiDefault_mobileNotDefault() = fun mobileIsDefault_wifiDefault_mobileNotDefault() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
@@ -888,6 +824,195 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
/** Regression test for b/272586234. */
@Test
fun hasCarrierMergedConnection_carrierMergedViaWifi_isTrue() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun hasCarrierMergedConnection_carrierMergedViaMobile_isTrue() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
yield()
assertThat(latest).isTrue()
job.cancel()
}
/** Regression test for b/272586234. */
@Test
fun hasCarrierMergedConnection_carrierMergedViaWifiWithVcnTransport_isTrue() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun hasCarrierMergedConnection_carrierMergedViaMobileWithVcnTransport_isTrue() =
runBlocking(IMMEDIATE) {
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun hasCarrierMergedConnection_isCarrierMergedViaUnderlyingWifi_isTrue() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
val underlyingNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via WIFI
// transport and WifiInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
yield()
// THEN there's a carrier merged connection
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun hasCarrierMergedConnection_isCarrierMergedViaUnderlyingCellular_isTrue() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via CELLULAR
// transport and VcnTransportInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
yield()
// THEN there's a carrier merged connection
assertThat(latest).isTrue()
job.cancel()
}
/** Regression test for b/272586234. */
@Test
fun hasCarrierMergedConnection_defaultNotCarrierMerged_butWifiRepoHasCarrierMerged_isTrue() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
val job = underTest.hasCarrierMergedConnection.onEach { latest = it }.launchIn(this)
// WHEN the default callback isn't carrier merged
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
val caps =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
yield()
// BUT the wifi repo has gotten updates that it *is* carrier merged
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
yield()
// THEN hasCarrierMergedConnection is true
assertThat(latest).isTrue()
job.cancel()
}
@Test @Test
fun defaultConnectionIsValidated_startsAsFalse() { fun defaultConnectionIsValidated_startsAsFalse() {
assertThat(underTest.defaultConnectionIsValidated.value).isFalse() assertThat(underTest.defaultConnectionIsValidated.value).isFalse()

View File

@@ -456,7 +456,50 @@ class MobileIconsInteractorTest : SysuiTestCase() {
} }
@Test @Test
fun mobileIsDefault_usesRepoValue() = fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
connectionsRepository.mobileIsDefault.value = false
connectionsRepository.hasCarrierMergedConnection.value = false
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.hasCarrierMergedConnection.value = false
assertThat(latest).isTrue()
job.cancel()
}
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
connectionsRepository.mobileIsDefault.value = false
connectionsRepository.hasCarrierMergedConnection.value = true
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun mobileIsDefault_updatesWhenRepoUpdates() =
testScope.runTest { testScope.runTest {
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this) val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
@@ -467,7 +510,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
connectionsRepository.mobileIsDefault.value = false connectionsRepository.mobileIsDefault.value = false
assertThat(latest).isFalse() assertThat(latest).isFalse()
connectionsRepository.mobileIsDefault.value = true connectionsRepository.hasCarrierMergedConnection.value = true
assertThat(latest).isTrue() assertThat(latest).isTrue()
job.cancel() job.cancel()

View File

@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlo
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.argumentCaptor
@@ -490,6 +491,111 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun defaultConnections_nullUnderlyingInfo_noError() {
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(null)
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// No assert, just verify no error
}
@Test
fun defaultConnections_underlyingInfoHasNullCapabilities_noError() {
val underlyingNetworkWithNull = mock<Network>()
whenever(connectivityManager.getNetworkCapabilities(underlyingNetworkWithNull))
.thenReturn(null)
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetworkWithNull))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// No assert, just verify no error
}
// This test verifies our internal API for completeness, but we don't expect this case to ever
// happen in practice.
@Test
fun defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefault() =
testScope.runTest {
var latest: DefaultConnectionModel? = null
val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
// Underlying carrier merged network
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// Main network with underlying network
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
job.cancel()
}
/** Test for b/225902574. */
@Test
fun defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefault() =
testScope.runTest {
var latest: DefaultConnectionModel? = null
val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
// Underlying carrier merged network
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// Main network with underlying network
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
job.cancel()
}
@Test @Test
fun defaultConnections_multipleTransports_multipleDefault() = fun defaultConnections_multipleTransports_multipleDefault() =
testScope.runTest { testScope.runTest {
@@ -548,6 +654,279 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun getMainOrUnderlyingWifiInfo_wifi_hasInfo() {
val wifiInfo = mock<WifiInfo>()
val capabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(wifiInfo)
}
val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
assertThat(result).isEqualTo(wifiInfo)
}
@Test
fun getMainOrUnderlyingWifiInfo_vcnWithWifi_hasInfo() {
val wifiInfo = mock<WifiInfo>()
val vcnInfo = VcnTransportInfo(wifiInfo)
val capabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(vcnInfo)
}
val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
assertThat(result).isEqualTo(wifiInfo)
}
@Test
fun getMainOrUnderlyingWifiInfo_notCellularOrWifiTransport_noInfo() {
val capabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
whenever(it.transportInfo).thenReturn(mock<WifiInfo>())
}
val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
assertThat(result).isNull()
}
@Test
fun getMainOrUnderlyingWifiInfo_cellular_underlyingWifi_hasInfo() {
val underlyingNetwork = mock<Network>()
val underlyingWifiInfo = mock<WifiInfo>()
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingWifiInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying wifi network
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN we fetch the underlying wifi info
assertThat(result).isEqualTo(underlyingWifiInfo)
}
@Test
fun getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfo() {
val underlyingNetwork = mock<Network>()
val underlyingWifiInfo = mock<WifiInfo>()
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingWifiInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying wifi network but is *not* CELLULAR
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN we DON'T fetch the underlying wifi info
assertThat(result).isNull()
}
@Test
fun getMainOrUnderlyingWifiInfo_cellular_underlyingVcnWithWifi_hasInfo() {
val wifiInfo = mock<WifiInfo>()
val underlyingNetwork = mock<Network>()
val underlyingVcnInfo = VcnTransportInfo(wifiInfo)
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingVcnInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying VCN network with wifi
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN we fetch the wifi info
assertThat(result).isEqualTo(wifiInfo)
}
@Test
fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() {
val underlyingNetwork = mock<Network>()
val underlyingVcnInfo = VcnTransportInfo(mock<WifiInfo>())
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingVcnInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying wifi network but it is *not* CELLULAR
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN we DON'T fetch the underlying wifi info
assertThat(result).isNull()
}
@Test
fun getMainOrUnderlyingWifiInfo_cellular_underlyingCellularWithCarrierMerged_hasInfo() {
// Underlying carrier merged network
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// Main network with underlying network
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCarrierMergedNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
assertThat(result).isEqualTo(carrierMergedInfo)
assertThat(result!!.isCarrierMerged).isTrue()
}
@Test
fun getMainOrUnderlyingWifiInfo_multipleUnderlying_usesFirstNonNull() {
// First underlying: Not wifi
val underlyingNotWifiNetwork = mock<Network>()
val underlyingNotWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
whenever(it.transportInfo).thenReturn(null)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNotWifiNetwork))
.thenReturn(underlyingNotWifiCapabilities)
// Second underlying: wifi
val underlyingWifiNetwork1 = mock<Network>()
val underlyingWifiInfo1 = mock<WifiInfo>()
val underlyingWifiCapabilities1 =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingWifiInfo1)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingWifiNetwork1))
.thenReturn(underlyingWifiCapabilities1)
// Third underlying: also wifi
val underlyingWifiNetwork2 = mock<Network>()
val underlyingWifiInfo2 = mock<WifiInfo>()
val underlyingWifiCapabilities2 =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(underlyingWifiInfo2)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingWifiNetwork2))
.thenReturn(underlyingWifiCapabilities2)
// WHEN the main capabilities has multiple underlying networks
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(
listOf(
underlyingNotWifiNetwork,
underlyingWifiNetwork1,
underlyingWifiNetwork2,
)
)
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN the first wifi one is used
assertThat(result).isEqualTo(underlyingWifiInfo1)
}
@Test
fun getMainOrUnderlyingWifiInfo_nestedUnderlying_doesNotLookAtNested() {
// WHEN there are two layers of underlying networks...
// Nested network
val nestedUnderlyingNetwork = mock<Network>()
val nestedWifiInfo = mock<WifiInfo>()
val nestedCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(nestedWifiInfo)
}
whenever(connectivityManager.getNetworkCapabilities(nestedUnderlyingNetwork))
.thenReturn(nestedCapabilities)
// Underlying network containing the nested network
val underlyingNetwork = mock<Network>()
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(nestedUnderlyingNetwork))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingCapabilities)
// Main network containing the underlying network, which contains the nested network
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
// THEN only the first layer is checked, and the first layer has no wifi info
assertThat(result).isNull()
}
private fun createAndSetRepo() { private fun createAndSetRepo() {
underTest = underTest =
ConnectivityRepositoryImpl( ConnectivityRepositoryImpl(

View File

@@ -400,7 +400,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
} }
@Test @Test
fun wifiNetwork_cellularAndWifiTransports_usesCellular_isTrue() = fun isWifiDefault_cellularAndWifiTransports_usesCellular_isTrue() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this) val job = underTest.isWifiDefault.launchIn(this)
@@ -436,6 +436,75 @@ class WifiRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun isWifiDefault_isCarrierMergedViaUnderlyingWifi_isTrue() =
runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
val underlyingNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply {
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
}
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via WIFI
// transport and WifiInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// THEN the wifi network is carrier merged, so wifi is default
assertThat(underTest.isWifiDefault.value).isTrue()
job.cancel()
}
@Test
fun isWifiDefault_isCarrierMergedViaUnderlyingCellular_isTrue() =
runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via CELLULAR
// transport and VcnTransportInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
}
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// THEN the wifi network is carrier merged, so wifi is default
assertThat(underTest.isWifiDefault.value).isTrue()
job.cancel()
}
@Test @Test
fun isWifiDefault_wifiNetworkLost_isFalse() = fun isWifiDefault_wifiNetworkLost_isFalse() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
@@ -510,6 +579,81 @@ class WifiRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun wifiNetwork_isCarrierMergedViaUnderlyingWifi_flowHasCarrierMerged() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
val underlyingNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply {
whenever(this.isCarrierMerged).thenReturn(true)
whenever(this.isPrimary).thenReturn(true)
}
val underlyingWifiCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(carrierMergedInfo)
}
whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
.thenReturn(underlyingWifiCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via WIFI
// transport and WifiInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
}
getNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// THEN the wifi network is carrier merged
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
job.cancel()
}
@Test
fun wifiNetwork_isCarrierMergedViaUnderlyingCellular_flowHasCarrierMerged() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
val underlyingCarrierMergedNetwork = mock<Network>()
val carrierMergedInfo =
mock<WifiInfo>().apply {
whenever(this.isCarrierMerged).thenReturn(true)
whenever(this.isPrimary).thenReturn(true)
}
val underlyingCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
}
whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
.thenReturn(underlyingCapabilities)
// WHEN the main capabilities have an underlying carrier merged network via CELLULAR
// transport and VcnTransportInfo
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
}
getNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
// THEN the wifi network is carrier merged
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
job.cancel()
}
@Test @Test
fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() = fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {