Merge changes I9414c0b2,I649b9198 into tm-qpr-dev am: aa972051b0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20855888 Change-Id: I481e4d25d72536f7829b9d3da9bd9f7e6cfb1196 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.model
|
||||
|
||||
import android.telephony.Annotation.NetworkType
|
||||
import com.android.settingslib.SignalIcon
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
|
||||
/**
|
||||
@@ -38,4 +40,12 @@ sealed interface ResolvedNetworkType {
|
||||
data class OverrideNetworkType(
|
||||
override val lookupKey: String,
|
||||
) : ResolvedNetworkType
|
||||
|
||||
/** Represents the carrier merged network. See [CarrierMergedConnectionRepository]. */
|
||||
object CarrierMergedNetworkType : ResolvedNetworkType {
|
||||
// Effectively unused since [iconGroupOverride] is used instead.
|
||||
override val lookupKey: String = "cwf"
|
||||
|
||||
val iconGroupOverride: SignalIcon.MobileIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.telephony.TelephonyManager
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
@@ -50,7 +49,7 @@ interface MobileConnectionRepository {
|
||||
* A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
|
||||
* listener + model.
|
||||
*/
|
||||
val connectionInfo: Flow<MobileConnectionModel>
|
||||
val connectionInfo: StateFlow<MobileConnectionModel>
|
||||
|
||||
/** The total number of levels. Used with [SignalDrawable]. */
|
||||
val numberOfLevels: StateFlow<Int>
|
||||
|
||||
@@ -39,7 +39,11 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.Mobile
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.CarrierMergedConnectionRepository.Companion.createCarrierMergedConnectionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -60,15 +64,19 @@ import kotlinx.coroutines.launch
|
||||
class DemoMobileConnectionsRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val dataSource: DemoModeMobileConnectionDataSource,
|
||||
private val mobileDataSource: DemoModeMobileConnectionDataSource,
|
||||
private val wifiDataSource: DemoModeWifiDataSource,
|
||||
@Application private val scope: CoroutineScope,
|
||||
context: Context,
|
||||
private val logFactory: TableLogBufferFactory,
|
||||
) : MobileConnectionsRepository {
|
||||
|
||||
private var demoCommandJob: Job? = null
|
||||
private var mobileDemoCommandJob: Job? = null
|
||||
private var wifiDemoCommandJob: Job? = null
|
||||
|
||||
private var connectionRepoCache = mutableMapOf<Int, DemoMobileConnectionRepository>()
|
||||
private var carrierMergedSubId: Int? = null
|
||||
|
||||
private var connectionRepoCache = mutableMapOf<Int, CacheContainer>()
|
||||
private val subscriptionInfoCache = mutableMapOf<Int, SubscriptionModel>()
|
||||
val demoModeFinishedEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
|
||||
|
||||
@@ -144,52 +152,83 @@ constructor(
|
||||
override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
|
||||
|
||||
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
|
||||
return connectionRepoCache[subId]
|
||||
?: createDemoMobileConnectionRepo(subId).also { connectionRepoCache[subId] = it }
|
||||
val current = connectionRepoCache[subId]?.repo
|
||||
if (current != null) {
|
||||
return current
|
||||
}
|
||||
|
||||
val new = createDemoMobileConnectionRepo(subId)
|
||||
connectionRepoCache[subId] = new
|
||||
return new.repo
|
||||
}
|
||||
|
||||
private fun createDemoMobileConnectionRepo(subId: Int): DemoMobileConnectionRepository {
|
||||
val tableLogBuffer = logFactory.getOrCreate("DemoMobileConnectionLog [$subId]", 100)
|
||||
private fun createDemoMobileConnectionRepo(subId: Int): CacheContainer {
|
||||
val tableLogBuffer =
|
||||
logFactory.getOrCreate(
|
||||
"DemoMobileConnectionLog [$subId]",
|
||||
MOBILE_CONNECTION_BUFFER_SIZE,
|
||||
)
|
||||
|
||||
return DemoMobileConnectionRepository(
|
||||
subId,
|
||||
tableLogBuffer,
|
||||
)
|
||||
val repo =
|
||||
DemoMobileConnectionRepository(
|
||||
subId,
|
||||
tableLogBuffer,
|
||||
)
|
||||
return CacheContainer(repo, lastMobileState = null)
|
||||
}
|
||||
|
||||
override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
|
||||
|
||||
fun startProcessingCommands() {
|
||||
demoCommandJob =
|
||||
mobileDemoCommandJob =
|
||||
scope.launch {
|
||||
dataSource.mobileEvents.filterNotNull().collect { event -> processEvent(event) }
|
||||
mobileDataSource.mobileEvents.filterNotNull().collect { event ->
|
||||
processMobileEvent(event)
|
||||
}
|
||||
}
|
||||
wifiDemoCommandJob =
|
||||
scope.launch {
|
||||
wifiDataSource.wifiEvents.filterNotNull().collect { event ->
|
||||
processWifiEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopProcessingCommands() {
|
||||
demoCommandJob?.cancel()
|
||||
mobileDemoCommandJob?.cancel()
|
||||
wifiDemoCommandJob?.cancel()
|
||||
_subscriptions.value = listOf()
|
||||
connectionRepoCache.clear()
|
||||
subscriptionInfoCache.clear()
|
||||
}
|
||||
|
||||
private fun processEvent(event: FakeNetworkEventModel) {
|
||||
private fun processMobileEvent(event: FakeNetworkEventModel) {
|
||||
when (event) {
|
||||
is Mobile -> {
|
||||
processEnabledMobileState(event)
|
||||
}
|
||||
is MobileDisabled -> {
|
||||
processDisabledMobileState(event)
|
||||
maybeRemoveSubscription(event.subId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processWifiEvent(event: FakeWifiEventModel) {
|
||||
when (event) {
|
||||
is FakeWifiEventModel.WifiDisabled -> disableCarrierMerged()
|
||||
is FakeWifiEventModel.Wifi -> disableCarrierMerged()
|
||||
is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processEnabledMobileState(state: Mobile) {
|
||||
// get or create the connection repo, and set its values
|
||||
val subId = state.subId ?: DEFAULT_SUB_ID
|
||||
maybeCreateSubscription(subId)
|
||||
|
||||
val connection = getRepoForSubId(subId)
|
||||
connectionRepoCache[subId]?.lastMobileState = state
|
||||
|
||||
// This is always true here, because we split out disabled states at the data-source level
|
||||
connection.dataEnabled.value = true
|
||||
connection.networkName.value = NetworkNameModel.Derived(state.name)
|
||||
@@ -198,14 +237,36 @@ constructor(
|
||||
connection.connectionInfo.value = state.toMobileConnectionModel()
|
||||
}
|
||||
|
||||
private fun processDisabledMobileState(state: MobileDisabled) {
|
||||
private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
|
||||
// The new carrier merged connection is for a different sub ID, so disable carrier merged
|
||||
// for the current (now old) sub
|
||||
if (carrierMergedSubId != event.subscriptionId) {
|
||||
disableCarrierMerged()
|
||||
}
|
||||
|
||||
// get or create the connection repo, and set its values
|
||||
val subId = event.subscriptionId
|
||||
maybeCreateSubscription(subId)
|
||||
carrierMergedSubId = subId
|
||||
|
||||
val connection = getRepoForSubId(subId)
|
||||
// This is always true here, because we split out disabled states at the data-source level
|
||||
connection.dataEnabled.value = true
|
||||
connection.networkName.value = NetworkNameModel.Derived(CARRIER_MERGED_NAME)
|
||||
connection.numberOfLevels.value = event.numberOfLevels
|
||||
connection.cdmaRoaming.value = false
|
||||
connection.connectionInfo.value = event.toMobileConnectionModel()
|
||||
Log.e("CCS", "output connection info = ${connection.connectionInfo.value}")
|
||||
}
|
||||
|
||||
private fun maybeRemoveSubscription(subId: Int?) {
|
||||
if (_subscriptions.value.isEmpty()) {
|
||||
// Nothing to do here
|
||||
return
|
||||
}
|
||||
|
||||
val subId =
|
||||
state.subId
|
||||
val finalSubId =
|
||||
subId
|
||||
?: run {
|
||||
// For sake of usability, we can allow for no subId arg if there is only one
|
||||
// subscription
|
||||
@@ -223,7 +284,21 @@ constructor(
|
||||
_subscriptions.value[0].subscriptionId
|
||||
}
|
||||
|
||||
removeSubscription(subId)
|
||||
removeSubscription(finalSubId)
|
||||
}
|
||||
|
||||
private fun disableCarrierMerged() {
|
||||
val currentCarrierMergedSubId = carrierMergedSubId ?: return
|
||||
|
||||
// If this sub ID was previously not carrier merged, we should reset it to its previous
|
||||
// connection.
|
||||
val lastMobileState = connectionRepoCache[carrierMergedSubId]?.lastMobileState
|
||||
if (lastMobileState != null) {
|
||||
processEnabledMobileState(lastMobileState)
|
||||
} else {
|
||||
// Otherwise, just remove the subscription entirely
|
||||
removeSubscription(currentCarrierMergedSubId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeSubscription(subId: Int) {
|
||||
@@ -251,6 +326,10 @@ constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun FakeWifiEventModel.CarrierMerged.toMobileConnectionModel(): MobileConnectionModel {
|
||||
return createCarrierMergedConnectionModel(this.level)
|
||||
}
|
||||
|
||||
private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType {
|
||||
val key = mobileMappingsReverseLookup.value[this] ?: "dis"
|
||||
return DefaultNetworkType(key)
|
||||
@@ -260,9 +339,17 @@ constructor(
|
||||
private const val TAG = "DemoMobileConnectionsRepo"
|
||||
|
||||
private const val DEFAULT_SUB_ID = 1
|
||||
|
||||
private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
|
||||
}
|
||||
}
|
||||
|
||||
class CacheContainer(
|
||||
var repo: DemoMobileConnectionRepository,
|
||||
/** The last received [Mobile] event. Used when switching from carrier merged back to mobile. */
|
||||
var lastMobileState: Mobile?,
|
||||
)
|
||||
|
||||
class DemoMobileConnectionRepository(
|
||||
override val subId: Int,
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobile.data.repository.prod
|
||||
|
||||
import android.util.Log
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
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
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
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.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* A repository implementation for a carrier merged (aka VCN) network. A carrier merged network is
|
||||
* delivered to SysUI as a wifi network (see [WifiNetworkModel.CarrierMerged], but is visually
|
||||
* displayed as a mobile network triangle.
|
||||
*
|
||||
* See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
|
||||
*
|
||||
* See [MobileConnectionRepositoryImpl] for a repository implementation of a typical mobile
|
||||
* connection.
|
||||
*/
|
||||
class CarrierMergedConnectionRepository(
|
||||
override val subId: Int,
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
@Application private val scope: CoroutineScope,
|
||||
val wifiRepository: WifiRepository,
|
||||
) : MobileConnectionRepository {
|
||||
|
||||
/**
|
||||
* Outputs the carrier merged network to use, or null if we don't have a valid carrier merged
|
||||
* network.
|
||||
*/
|
||||
private val network: Flow<WifiNetworkModel.CarrierMerged?> =
|
||||
combine(
|
||||
wifiRepository.isWifiEnabled,
|
||||
wifiRepository.isWifiDefault,
|
||||
wifiRepository.wifiNetwork,
|
||||
) { isEnabled, isDefault, network ->
|
||||
when {
|
||||
!isEnabled -> null
|
||||
!isDefault -> null
|
||||
network !is WifiNetworkModel.CarrierMerged -> null
|
||||
network.subscriptionId != subId -> {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Connection repo subId=$subId " +
|
||||
"does not equal wifi repo subId=${network.subscriptionId}; " +
|
||||
"not showing carrier merged"
|
||||
)
|
||||
null
|
||||
}
|
||||
else -> network
|
||||
}
|
||||
}
|
||||
|
||||
override val connectionInfo: StateFlow<MobileConnectionModel> =
|
||||
network
|
||||
.map { it.toMobileConnectionModel() }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectionModel())
|
||||
|
||||
// TODO(b/238425913): Add logging to this class.
|
||||
// TODO(b/238425913): Make sure SignalStrength.getEmptyState is used when appropriate.
|
||||
|
||||
// Carrier merged is never roaming.
|
||||
override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
|
||||
|
||||
// TODO(b/238425913): Fetch the carrier merged network name.
|
||||
override val networkName: StateFlow<NetworkNameModel> =
|
||||
flowOf(defaultNetworkName)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
|
||||
|
||||
override val numberOfLevels: StateFlow<Int> =
|
||||
wifiRepository.wifiNetwork
|
||||
.map {
|
||||
if (it is WifiNetworkModel.CarrierMerged) {
|
||||
it.numberOfLevels
|
||||
} else {
|
||||
DEFAULT_NUM_LEVELS
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
|
||||
|
||||
override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
|
||||
|
||||
private fun WifiNetworkModel.CarrierMerged?.toMobileConnectionModel(): MobileConnectionModel {
|
||||
if (this == null) {
|
||||
return MobileConnectionModel()
|
||||
}
|
||||
|
||||
return createCarrierMergedConnectionModel(level)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an instance of [MobileConnectionModel] that represents a carrier merged network
|
||||
* with the given [level].
|
||||
*/
|
||||
fun createCarrierMergedConnectionModel(level: Int): MobileConnectionModel {
|
||||
return MobileConnectionModel(
|
||||
primaryLevel = level,
|
||||
cdmaLevel = level,
|
||||
// A [WifiNetworkModel.CarrierMerged] instance is always connected.
|
||||
// (A [WifiNetworkModel.Inactive] represents a disconnected network.)
|
||||
dataConnectionState = DataConnectionState.Connected,
|
||||
// TODO(b/238425913): This should come from [WifiRepository.wifiActivity].
|
||||
dataActivityDirection =
|
||||
DataActivityModel(
|
||||
hasActivityIn = false,
|
||||
hasActivityOut = false,
|
||||
),
|
||||
resolvedNetworkType = ResolvedNetworkType.CarrierMergedNetworkType,
|
||||
// Carrier merged is never roaming
|
||||
isRoaming = false,
|
||||
|
||||
// TODO(b/238425913): Verify that these fields never change for carrier merged.
|
||||
isEmergencyOnly = false,
|
||||
operatorAlphaShort = null,
|
||||
isInService = true,
|
||||
isGsm = false,
|
||||
carrierNetworkChangeActive = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class Factory
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val wifiRepository: WifiRepository,
|
||||
) {
|
||||
fun build(
|
||||
subId: Int,
|
||||
mobileLogger: TableLogBuffer,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
): MobileConnectionRepository {
|
||||
return CarrierMergedConnectionRepository(
|
||||
subId,
|
||||
mobileLogger,
|
||||
defaultNetworkName,
|
||||
scope,
|
||||
wifiRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "CarrierMergedConnectionRepository"
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobile.data.repository.prod
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
import com.android.systemui.log.table.logDiffsForTable
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* A repository that fully implements a mobile connection.
|
||||
*
|
||||
* This connection could either be a typical mobile connection (see [MobileConnectionRepositoryImpl]
|
||||
* or a carrier merged connection (see [CarrierMergedConnectionRepository]). This repository
|
||||
* switches between the two types of connections based on whether the connection is currently
|
||||
* carrier merged (see [setIsCarrierMerged]).
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class FullMobileConnectionRepository(
|
||||
override val subId: Int,
|
||||
startingIsCarrierMerged: Boolean,
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
private val defaultNetworkName: NetworkNameModel,
|
||||
private val networkNameSeparator: String,
|
||||
private val globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
@Application scope: CoroutineScope,
|
||||
private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
|
||||
private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
|
||||
) : MobileConnectionRepository {
|
||||
/**
|
||||
* Sets whether this connection is a typical mobile connection or a carrier merged connection.
|
||||
*/
|
||||
fun setIsCarrierMerged(isCarrierMerged: Boolean) {
|
||||
_isCarrierMerged.value = isCarrierMerged
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this repo is currently for a carrier merged connection and false otherwise.
|
||||
*/
|
||||
@VisibleForTesting fun getIsCarrierMerged() = _isCarrierMerged.value
|
||||
|
||||
private val _isCarrierMerged = MutableStateFlow(startingIsCarrierMerged)
|
||||
private val isCarrierMerged: StateFlow<Boolean> =
|
||||
_isCarrierMerged
|
||||
.logDiffsForTable(
|
||||
tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "isCarrierMerged",
|
||||
initialValue = startingIsCarrierMerged,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), startingIsCarrierMerged)
|
||||
|
||||
private val mobileRepo: MobileConnectionRepository by lazy {
|
||||
mobileRepoFactory.build(
|
||||
subId,
|
||||
tableLogBuffer,
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private val carrierMergedRepo: MobileConnectionRepository by lazy {
|
||||
carrierMergedRepoFactory.build(subId, tableLogBuffer, defaultNetworkName)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal val activeRepo: StateFlow<MobileConnectionRepository> = run {
|
||||
val initial =
|
||||
if (startingIsCarrierMerged) {
|
||||
carrierMergedRepo
|
||||
} else {
|
||||
mobileRepo
|
||||
}
|
||||
|
||||
this.isCarrierMerged
|
||||
.mapLatest { isCarrierMerged ->
|
||||
if (isCarrierMerged) {
|
||||
carrierMergedRepo
|
||||
} else {
|
||||
mobileRepo
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
override val cdmaRoaming =
|
||||
activeRepo
|
||||
.flatMapLatest { it.cdmaRoaming }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaRoaming.value)
|
||||
|
||||
override val connectionInfo =
|
||||
activeRepo
|
||||
.flatMapLatest { it.connectionInfo }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.connectionInfo.value)
|
||||
|
||||
override val dataEnabled =
|
||||
activeRepo
|
||||
.flatMapLatest { it.dataEnabled }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
|
||||
|
||||
override val numberOfLevels =
|
||||
activeRepo
|
||||
.flatMapLatest { it.numberOfLevels }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.numberOfLevels.value)
|
||||
|
||||
override val networkName =
|
||||
activeRepo
|
||||
.flatMapLatest { it.networkName }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
|
||||
|
||||
class Factory
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val logFactory: TableLogBufferFactory,
|
||||
private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
|
||||
private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
|
||||
) {
|
||||
fun build(
|
||||
subId: Int,
|
||||
startingIsCarrierMerged: Boolean,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
networkNameSeparator: String,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
): FullMobileConnectionRepository {
|
||||
val mobileLogger =
|
||||
logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
|
||||
|
||||
return FullMobileConnectionRepository(
|
||||
subId,
|
||||
startingIsCarrierMerged,
|
||||
mobileLogger,
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
scope,
|
||||
mobileRepoFactory,
|
||||
carrierMergedRepoFactory,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** The buffer size to use for logging. */
|
||||
const val MOBILE_CONNECTION_BUFFER_SIZE = 100
|
||||
|
||||
/** Returns a log buffer name for a mobile connection with the given [subId]. */
|
||||
fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
import com.android.systemui.log.table.logDiffsForTable
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
@@ -70,6 +69,10 @@ import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* A repository implementation for a typical mobile connection (as opposed to a carrier merged
|
||||
* connection -- see [CarrierMergedConnectionRepository]).
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MobileConnectionRepositoryImpl(
|
||||
@@ -298,18 +301,16 @@ class MobileConnectionRepositoryImpl(
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val mobileMappingsProxy: MobileMappingsProxy,
|
||||
private val logFactory: TableLogBufferFactory,
|
||||
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||
@Application private val scope: CoroutineScope,
|
||||
) {
|
||||
fun build(
|
||||
subId: Int,
|
||||
mobileLogger: TableLogBuffer,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
networkNameSeparator: String,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
): MobileConnectionRepository {
|
||||
val mobileLogger = logFactory.getOrCreate(tableBufferLogName(subId), 100)
|
||||
|
||||
return MobileConnectionRepositoryImpl(
|
||||
context,
|
||||
subId,
|
||||
@@ -327,8 +328,4 @@ class MobileConnectionRepositoryImpl(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,12 @@ import com.android.systemui.dagger.qualifiers.Background
|
||||
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.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -85,9 +86,14 @@ constructor(
|
||||
private val context: Context,
|
||||
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val mobileConnectionRepositoryFactory: MobileConnectionRepositoryImpl.Factory
|
||||
// Some "wifi networks" should be rendered as a mobile connection, which is why the wifi
|
||||
// repository is an input to the mobile repository.
|
||||
// See [CarrierMergedConnectionRepository] for details.
|
||||
wifiRepository: WifiRepository,
|
||||
private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
|
||||
) : MobileConnectionsRepository {
|
||||
private var subIdRepositoryCache: MutableMap<Int, MobileConnectionRepository> = mutableMapOf()
|
||||
private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
|
||||
mutableMapOf()
|
||||
|
||||
private val defaultNetworkName =
|
||||
NetworkNameModel.Default(
|
||||
@@ -97,30 +103,43 @@ constructor(
|
||||
private val networkNameSeparator: String =
|
||||
context.getString(R.string.status_bar_network_name_separator)
|
||||
|
||||
private val carrierMergedSubId: StateFlow<Int?> =
|
||||
wifiRepository.wifiNetwork
|
||||
.mapLatest {
|
||||
if (it is WifiNetworkModel.CarrierMerged) {
|
||||
it.subscriptionId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : SubscriptionManager.OnSubscriptionsChangedListener() {
|
||||
override fun onSubscriptionsChanged() {
|
||||
trySend(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
subscriptionManager.addOnSubscriptionsChangedListener(
|
||||
bgDispatcher.asExecutor(),
|
||||
callback,
|
||||
)
|
||||
|
||||
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
|
||||
}
|
||||
|
||||
/**
|
||||
* State flow that emits the set of mobile data subscriptions, each represented by its own
|
||||
* [SubscriptionInfo]. We probably only need the [SubscriptionInfo.getSubscriptionId] of each
|
||||
* info object, but for now we keep track of the infos themselves.
|
||||
* [SubscriptionModel].
|
||||
*/
|
||||
override val subscriptions: StateFlow<List<SubscriptionModel>> =
|
||||
conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : SubscriptionManager.OnSubscriptionsChangedListener() {
|
||||
override fun onSubscriptionsChanged() {
|
||||
trySend(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
subscriptionManager.addOnSubscriptionsChangedListener(
|
||||
bgDispatcher.asExecutor(),
|
||||
callback,
|
||||
)
|
||||
|
||||
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
|
||||
}
|
||||
merge(mobileSubscriptionsChangeEvent, carrierMergedSubId)
|
||||
.mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } }
|
||||
.logInputChange(logger, "onSubscriptionsChanged")
|
||||
.onEach { infos -> dropUnusedReposFromCache(infos) }
|
||||
.onEach { infos -> updateRepos(infos) }
|
||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
|
||||
|
||||
/** StateFlow that keeps track of the current active mobile data subscription */
|
||||
@@ -173,7 +192,7 @@ constructor(
|
||||
.distinctUntilChanged()
|
||||
.logInputChange(logger, "defaultMobileIconGroup")
|
||||
|
||||
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
|
||||
override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
|
||||
if (!isValidSubId(subId)) {
|
||||
throw IllegalArgumentException(
|
||||
"subscriptionId $subId is not in the list of valid subscriptions"
|
||||
@@ -251,15 +270,27 @@ constructor(
|
||||
|
||||
@VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
|
||||
|
||||
private fun createRepositoryForSubId(subId: Int): MobileConnectionRepository {
|
||||
return mobileConnectionRepositoryFactory.build(
|
||||
private fun createRepositoryForSubId(subId: Int): FullMobileConnectionRepository {
|
||||
return fullMobileRepoFactory.build(
|
||||
subId,
|
||||
isCarrierMerged(subId),
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateRepos(newInfos: List<SubscriptionModel>) {
|
||||
dropUnusedReposFromCache(newInfos)
|
||||
subIdRepositoryCache.forEach { (subId, repo) ->
|
||||
repo.setIsCarrierMerged(isCarrierMerged(subId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCarrierMerged(subId: Int): Boolean {
|
||||
return subId == carrierMergedSubId.value
|
||||
}
|
||||
|
||||
private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) {
|
||||
// Remove any connection repository from the cache that isn't in the new set of IDs. They
|
||||
// will get garbage collected once their subscribers go away
|
||||
|
||||
@@ -22,8 +22,8 @@ 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.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -138,7 +138,11 @@ class MobileIconInteractorImpl(
|
||||
defaultMobileIconMapping,
|
||||
defaultMobileIconGroup,
|
||||
) { info, mapping, defaultGroup ->
|
||||
mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
|
||||
when (info.resolvedNetworkType) {
|
||||
is ResolvedNetworkType.CarrierMergedNetworkType ->
|
||||
info.resolvedNetworkType.iconGroupOverride
|
||||
else -> mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
|
||||
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.model
|
||||
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.log.table.TableRowLogger
|
||||
import com.android.systemui.log.table.Diffable
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
|
||||
|
||||
/** Provides information about the current wifi network. */
|
||||
sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
|
||||
// TODO(b/238425913): Have a better, more unified strategy for diff-logging instead of
|
||||
// copy-pasting the column names for each sub-object.
|
||||
|
||||
/**
|
||||
* A model representing that we couldn't fetch any wifi information.
|
||||
*
|
||||
@@ -41,8 +46,43 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
override fun logFull(row: TableRowLogger) {
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_UNAVAILABLE)
|
||||
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
|
||||
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
|
||||
row.logChange(COL_VALIDATED, false)
|
||||
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
|
||||
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
|
||||
row.logChange(COL_SSID, null)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, false)
|
||||
row.logChange(COL_PASSPOINT_NAME, null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model representing that the wifi information we received was invalid in some way.
|
||||
*/
|
||||
data class Invalid(
|
||||
/** A description of why the wifi information was invalid. */
|
||||
val invalidReason: String,
|
||||
) : WifiNetworkModel() {
|
||||
override fun toString() = "WifiNetwork.Invalid[$invalidReason]"
|
||||
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
|
||||
if (prevVal !is Invalid) {
|
||||
logFull(row)
|
||||
return
|
||||
}
|
||||
|
||||
if (invalidReason != prevVal.invalidReason) {
|
||||
row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
|
||||
}
|
||||
}
|
||||
|
||||
override fun logFull(row: TableRowLogger) {
|
||||
row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
|
||||
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
|
||||
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
|
||||
row.logChange(COL_VALIDATED, false)
|
||||
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
|
||||
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
|
||||
row.logChange(COL_SSID, null)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, false)
|
||||
@@ -59,18 +99,21 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
return
|
||||
}
|
||||
|
||||
if (prevVal is CarrierMerged) {
|
||||
// The only difference between CarrierMerged and Inactive is the type
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
|
||||
return
|
||||
}
|
||||
|
||||
// When changing from Active to Inactive, we need to log diffs to all the fields.
|
||||
logFullNonActiveNetwork(TYPE_INACTIVE, row)
|
||||
// When changing to Inactive, we need to log diffs to all the fields.
|
||||
logFull(row)
|
||||
}
|
||||
|
||||
override fun logFull(row: TableRowLogger) {
|
||||
logFullNonActiveNetwork(TYPE_INACTIVE, row)
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
|
||||
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
|
||||
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
|
||||
row.logChange(COL_VALIDATED, false)
|
||||
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
|
||||
row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
|
||||
row.logChange(COL_SSID, null)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, false)
|
||||
row.logChange(COL_PASSPOINT_NAME, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,22 +123,75 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
*
|
||||
* See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
|
||||
*/
|
||||
object CarrierMerged : WifiNetworkModel() {
|
||||
override fun toString() = "WifiNetwork.CarrierMerged"
|
||||
data class CarrierMerged(
|
||||
/**
|
||||
* The [android.net.Network.netId] we received from
|
||||
* [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
|
||||
*
|
||||
* Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
|
||||
*/
|
||||
val networkId: Int,
|
||||
|
||||
/**
|
||||
* The subscription ID that this connection represents.
|
||||
*
|
||||
* Comes from [android.net.wifi.WifiInfo.getSubscriptionId].
|
||||
*
|
||||
* Per that method, this value must not be [INVALID_SUBSCRIPTION_ID] (if it was invalid,
|
||||
* then this is *not* a carrier merged network).
|
||||
*/
|
||||
val subscriptionId: Int,
|
||||
|
||||
/**
|
||||
* The signal level, guaranteed to be 0 <= level <= numberOfLevels.
|
||||
*/
|
||||
val level: Int,
|
||||
|
||||
/**
|
||||
* The maximum possible level.
|
||||
*/
|
||||
val numberOfLevels: Int = DEFAULT_NUM_LEVELS,
|
||||
) : WifiNetworkModel() {
|
||||
init {
|
||||
require(level in MIN_VALID_LEVEL..numberOfLevels) {
|
||||
"0 <= wifi level <= $numberOfLevels required; level was $level"
|
||||
}
|
||||
require(subscriptionId != INVALID_SUBSCRIPTION_ID) {
|
||||
"subscription ID cannot be invalid"
|
||||
}
|
||||
}
|
||||
|
||||
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
|
||||
if (prevVal is CarrierMerged) {
|
||||
if (prevVal !is CarrierMerged) {
|
||||
logFull(row)
|
||||
return
|
||||
}
|
||||
|
||||
if (prevVal is Inactive) {
|
||||
// The only difference between CarrierMerged and Inactive is the type.
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
|
||||
return
|
||||
if (prevVal.networkId != networkId) {
|
||||
row.logChange(COL_NETWORK_ID, networkId)
|
||||
}
|
||||
if (prevVal.subscriptionId != subscriptionId) {
|
||||
row.logChange(COL_SUB_ID, subscriptionId)
|
||||
}
|
||||
if (prevVal.level != level) {
|
||||
row.logChange(COL_LEVEL, level)
|
||||
}
|
||||
if (prevVal.numberOfLevels != numberOfLevels) {
|
||||
row.logChange(COL_NUM_LEVELS, numberOfLevels)
|
||||
}
|
||||
}
|
||||
|
||||
// When changing from Active to CarrierMerged, we need to log diffs to all the fields.
|
||||
logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row)
|
||||
override fun logFull(row: TableRowLogger) {
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
|
||||
row.logChange(COL_NETWORK_ID, networkId)
|
||||
row.logChange(COL_SUB_ID, subscriptionId)
|
||||
row.logChange(COL_VALIDATED, true)
|
||||
row.logChange(COL_LEVEL, level)
|
||||
row.logChange(COL_NUM_LEVELS, numberOfLevels)
|
||||
row.logChange(COL_SSID, null)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, false)
|
||||
row.logChange(COL_PASSPOINT_NAME, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,38 +233,50 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
|
||||
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
|
||||
if (prevVal !is Active) {
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
|
||||
logFull(row)
|
||||
return
|
||||
}
|
||||
|
||||
if (prevVal !is Active || prevVal.networkId != networkId) {
|
||||
if (prevVal.networkId != networkId) {
|
||||
row.logChange(COL_NETWORK_ID, networkId)
|
||||
}
|
||||
if (prevVal !is Active || prevVal.isValidated != isValidated) {
|
||||
if (prevVal.isValidated != isValidated) {
|
||||
row.logChange(COL_VALIDATED, isValidated)
|
||||
}
|
||||
if (prevVal !is Active || prevVal.level != level) {
|
||||
if (prevVal.level != level) {
|
||||
row.logChange(COL_LEVEL, level)
|
||||
}
|
||||
if (prevVal !is Active || prevVal.ssid != ssid) {
|
||||
if (prevVal.ssid != ssid) {
|
||||
row.logChange(COL_SSID, ssid)
|
||||
}
|
||||
|
||||
// TODO(b/238425913): The passpoint-related values are frequently never used, so it
|
||||
// would be great to not log them when they're not used.
|
||||
if (prevVal !is Active || prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
|
||||
if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
|
||||
}
|
||||
if (prevVal !is Active ||
|
||||
prevVal.isOnlineSignUpForPasspointAccessPoint !=
|
||||
if (prevVal.isOnlineSignUpForPasspointAccessPoint !=
|
||||
isOnlineSignUpForPasspointAccessPoint) {
|
||||
row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
|
||||
}
|
||||
if (prevVal !is Active ||
|
||||
prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
|
||||
if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
|
||||
row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun logFull(row: TableRowLogger) {
|
||||
row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
|
||||
row.logChange(COL_NETWORK_ID, networkId)
|
||||
row.logChange(COL_SUB_ID, null)
|
||||
row.logChange(COL_VALIDATED, isValidated)
|
||||
row.logChange(COL_LEVEL, level)
|
||||
row.logChange(COL_NUM_LEVELS, null)
|
||||
row.logChange(COL_SSID, ssid)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
|
||||
row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
// Only include the passpoint-related values in the string if we have them. (Most
|
||||
// networks won't have them so they'll be mostly clutter.)
|
||||
@@ -188,22 +296,14 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
internal const val MIN_VALID_LEVEL = 0
|
||||
@VisibleForTesting
|
||||
internal const val MAX_VALID_LEVEL = 4
|
||||
}
|
||||
}
|
||||
|
||||
internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) {
|
||||
row.logChange(COL_NETWORK_TYPE, type)
|
||||
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
|
||||
row.logChange(COL_VALIDATED, false)
|
||||
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
|
||||
row.logChange(COL_SSID, null)
|
||||
row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
|
||||
row.logChange(COL_ONLINE_SIGN_UP, false)
|
||||
row.logChange(COL_PASSPOINT_NAME, null)
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
internal const val MIN_VALID_LEVEL = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,12 +314,16 @@ const val TYPE_ACTIVE = "Active"
|
||||
|
||||
const val COL_NETWORK_TYPE = "type"
|
||||
const val COL_NETWORK_ID = "networkId"
|
||||
const val COL_SUB_ID = "subscriptionId"
|
||||
const val COL_VALIDATED = "isValidated"
|
||||
const val COL_LEVEL = "level"
|
||||
const val COL_NUM_LEVELS = "maxLevel"
|
||||
const val COL_SSID = "ssid"
|
||||
const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
|
||||
const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
|
||||
const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
|
||||
|
||||
val LEVEL_DEFAULT: String? = null
|
||||
val NUM_LEVELS_DEFAULT: String? = null
|
||||
val NETWORK_ID_DEFAULT: String? = null
|
||||
val SUB_ID_DEFAULT: String? = null
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
|
||||
import com.android.systemui.demomode.DemoModeController
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -43,10 +44,10 @@ constructor(
|
||||
|
||||
private fun Bundle.toWifiEvent(): FakeWifiEventModel? {
|
||||
val wifi = getString("wifi") ?: return null
|
||||
return if (wifi == "show") {
|
||||
activeWifiEvent()
|
||||
} else {
|
||||
FakeWifiEventModel.WifiDisabled
|
||||
return when (wifi) {
|
||||
"show" -> activeWifiEvent()
|
||||
"carriermerged" -> carrierMergedWifiEvent()
|
||||
else -> FakeWifiEventModel.WifiDisabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +65,14 @@ constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun Bundle.carrierMergedWifiEvent(): FakeWifiEventModel.CarrierMerged {
|
||||
val subId = getString("slot")?.toInt() ?: DEFAULT_CARRIER_MERGED_SUB_ID
|
||||
val level = getString("level")?.toInt() ?: 0
|
||||
val numberOfLevels = getString("numlevels")?.toInt() ?: DEFAULT_NUM_LEVELS
|
||||
|
||||
return FakeWifiEventModel.CarrierMerged(subId, level, numberOfLevels)
|
||||
}
|
||||
|
||||
private fun String.toActivity(): Int =
|
||||
when (this) {
|
||||
"inout" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT
|
||||
@@ -71,4 +80,8 @@ constructor(
|
||||
"out" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT
|
||||
else -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_CARRIER_MERGED_SUB_ID = 10
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ constructor(
|
||||
private fun processEvent(event: FakeWifiEventModel) =
|
||||
when (event) {
|
||||
is FakeWifiEventModel.Wifi -> processEnabledWifiState(event)
|
||||
is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
|
||||
is FakeWifiEventModel.WifiDisabled -> processDisabledWifiState()
|
||||
}
|
||||
|
||||
@@ -85,6 +86,14 @@ constructor(
|
||||
_wifiNetwork.value = event.toWifiNetworkModel()
|
||||
}
|
||||
|
||||
private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
|
||||
_isWifiEnabled.value = true
|
||||
_isWifiDefault.value = true
|
||||
// TODO(b/238425913): Support activity in demo mode.
|
||||
_wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
|
||||
_wifiNetwork.value = event.toCarrierMergedModel()
|
||||
}
|
||||
|
||||
private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
|
||||
WifiNetworkModel.Active(
|
||||
networkId = DEMO_NET_ID,
|
||||
@@ -99,6 +108,14 @@ constructor(
|
||||
passpointProviderFriendlyName = null,
|
||||
)
|
||||
|
||||
private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = DEMO_NET_ID,
|
||||
subscriptionId = subscriptionId,
|
||||
level = level,
|
||||
numberOfLevels = numberOfLevels,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val DEMO_NET_ID = 1234
|
||||
}
|
||||
|
||||
@@ -29,5 +29,11 @@ sealed interface FakeWifiEventModel {
|
||||
val validated: Boolean?,
|
||||
) : FakeWifiEventModel
|
||||
|
||||
data class CarrierMerged(
|
||||
val subscriptionId: Int,
|
||||
val level: Int,
|
||||
val numberOfLevels: Int,
|
||||
) : FakeWifiEventModel
|
||||
|
||||
object WifiDisabled : FakeWifiEventModel
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.net.NetworkRequest
|
||||
import android.net.wifi.WifiInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiManager.TrafficStateCallback
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
@@ -269,7 +270,19 @@ constructor(
|
||||
wifiManager: WifiManager,
|
||||
): WifiNetworkModel {
|
||||
return if (wifiInfo.isCarrierMerged) {
|
||||
WifiNetworkModel.CarrierMerged
|
||||
if (wifiInfo.subscriptionId == INVALID_SUBSCRIPTION_ID) {
|
||||
WifiNetworkModel.Invalid(CARRIER_MERGED_INVALID_SUB_ID_REASON)
|
||||
} else {
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = network.getNetId(),
|
||||
subscriptionId = wifiInfo.subscriptionId,
|
||||
level = wifiManager.calculateSignalLevel(wifiInfo.rssi),
|
||||
// The WiFi signal level returned by WifiManager#calculateSignalLevel start
|
||||
// from 0, so WifiManager#getMaxSignalLevel + 1 represents the total level
|
||||
// buckets count.
|
||||
numberOfLevels = wifiManager.maxSignalLevel + 1,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
WifiNetworkModel.Active(
|
||||
network.getNetId(),
|
||||
@@ -302,6 +315,9 @@ constructor(
|
||||
.build()
|
||||
|
||||
private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
|
||||
|
||||
private const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
|
||||
"Wifi network was carrier merged but had invalid sub ID"
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
|
||||
@@ -66,6 +66,7 @@ class WifiInteractorImpl @Inject constructor(
|
||||
override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
|
||||
when (info) {
|
||||
is WifiNetworkModel.Unavailable -> null
|
||||
is WifiNetworkModel.Invalid -> null
|
||||
is WifiNetworkModel.Inactive -> null
|
||||
is WifiNetworkModel.CarrierMerged -> null
|
||||
is WifiNetworkModel.Active -> when {
|
||||
|
||||
@@ -83,6 +83,7 @@ constructor(
|
||||
private fun WifiNetworkModel.icon(): WifiIcon {
|
||||
return when (this) {
|
||||
is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
|
||||
is WifiNetworkModel.Invalid -> WifiIcon.Hidden
|
||||
is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
|
||||
is WifiNetworkModel.Inactive -> WifiIcon.Visible(
|
||||
res = WIFI_NO_NETWORK,
|
||||
|
||||
@@ -34,6 +34,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.valid
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
@@ -71,8 +73,10 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileRepositorySwitcher
|
||||
private lateinit var realRepo: MobileConnectionsRepositoryImpl
|
||||
private lateinit var demoRepo: DemoMobileConnectionsRepository
|
||||
private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var wifiDataSource: DemoModeWifiDataSource
|
||||
private lateinit var logFactory: TableLogBufferFactory
|
||||
private lateinit var wifiRepository: FakeWifiRepository
|
||||
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var subscriptionManager: SubscriptionManager
|
||||
@@ -96,10 +100,15 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
// Never start in demo mode
|
||||
whenever(demoModeController.isInDemoMode).thenReturn(false)
|
||||
|
||||
mockDataSource =
|
||||
mobileDataSource =
|
||||
mock<DemoModeMobileConnectionDataSource>().also {
|
||||
whenever(it.mobileEvents).thenReturn(fakeNetworkEventsFlow)
|
||||
}
|
||||
wifiDataSource =
|
||||
mock<DemoModeWifiDataSource>().also {
|
||||
whenever(it.wifiEvents).thenReturn(MutableStateFlow(null))
|
||||
}
|
||||
wifiRepository = FakeWifiRepository()
|
||||
|
||||
realRepo =
|
||||
MobileConnectionsRepositoryImpl(
|
||||
@@ -113,12 +122,14 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
wifiRepository,
|
||||
mock(),
|
||||
)
|
||||
|
||||
demoRepo =
|
||||
DemoMobileConnectionsRepository(
|
||||
dataSource = mockDataSource,
|
||||
mobileDataSource = mobileDataSource,
|
||||
wifiDataSource = wifiDataSource,
|
||||
scope = scope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectio
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
@@ -63,10 +65,12 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
|
||||
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
|
||||
|
||||
private lateinit var connectionsRepo: DemoMobileConnectionsRepository
|
||||
private lateinit var underTest: DemoMobileConnectionRepository
|
||||
private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var mockWifiDataSource: DemoModeWifiDataSource
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -75,10 +79,15 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
mock<DemoModeMobileConnectionDataSource>().also {
|
||||
whenever(it.mobileEvents).thenReturn(fakeNetworkEventFlow)
|
||||
}
|
||||
mockWifiDataSource =
|
||||
mock<DemoModeWifiDataSource>().also {
|
||||
whenever(it.wifiEvents).thenReturn(fakeWifiEventFlow)
|
||||
}
|
||||
|
||||
connectionsRepo =
|
||||
DemoMobileConnectionsRepository(
|
||||
dataSource = mockDataSource,
|
||||
mobileDataSource = mockDataSource,
|
||||
wifiDataSource = mockWifiDataSource,
|
||||
scope = testScope.backgroundScope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
|
||||
@@ -32,6 +32,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
@@ -57,21 +59,28 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
|
||||
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
|
||||
|
||||
private lateinit var underTest: DemoMobileConnectionsRepository
|
||||
private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var wifiDataSource: DemoModeWifiDataSource
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// The data source only provides one API, so we can mock it with a flow here for convenience
|
||||
mockDataSource =
|
||||
mobileDataSource =
|
||||
mock<DemoModeMobileConnectionDataSource>().also {
|
||||
whenever(it.mobileEvents).thenReturn(fakeNetworkEventFlow)
|
||||
}
|
||||
wifiDataSource =
|
||||
mock<DemoModeWifiDataSource>().also {
|
||||
whenever(it.wifiEvents).thenReturn(fakeWifiEventFlow)
|
||||
}
|
||||
|
||||
underTest =
|
||||
DemoMobileConnectionsRepository(
|
||||
dataSource = mockDataSource,
|
||||
mobileDataSource = mobileDataSource,
|
||||
wifiDataSource = wifiDataSource,
|
||||
scope = testScope.backgroundScope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
@@ -96,6 +105,22 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wifi carrier merged event - create new subscription`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEmpty()
|
||||
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5)
|
||||
|
||||
assertThat(latest).hasSize(1)
|
||||
assertThat(latest!![0].subscriptionId).isEqualTo(5)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network event - reuses subscription when same Id`() =
|
||||
testScope.runTest {
|
||||
@@ -118,6 +143,28 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wifi carrier merged event - reuses subscription when same Id`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEmpty()
|
||||
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5, level = 1)
|
||||
|
||||
assertThat(latest).hasSize(1)
|
||||
assertThat(latest!![0].subscriptionId).isEqualTo(5)
|
||||
|
||||
// Second network event comes in with the same subId, does not create a new subscription
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5, level = 2)
|
||||
|
||||
assertThat(latest).hasSize(1)
|
||||
assertThat(latest!![0].subscriptionId).isEqualTo(5)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple subscriptions`() =
|
||||
testScope.runTest {
|
||||
@@ -132,6 +179,35 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile subscription and carrier merged subscription`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
fakeNetworkEventFlow.value = validMobileEvent(subId = 1)
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 5)
|
||||
|
||||
assertThat(latest).hasSize(2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple mobile subscriptions and carrier merged subscription`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
fakeNetworkEventFlow.value = validMobileEvent(subId = 1)
|
||||
fakeNetworkEventFlow.value = validMobileEvent(subId = 2)
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 3)
|
||||
|
||||
assertThat(latest).hasSize(3)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile disabled event - disables connection - subId specified - single conn`() =
|
||||
testScope.runTest {
|
||||
@@ -194,6 +270,112 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wifi network updates to disabled - carrier merged connection removed`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 1)
|
||||
|
||||
assertThat(latest).hasSize(1)
|
||||
|
||||
fakeWifiEventFlow.value = FakeWifiEventModel.WifiDisabled
|
||||
|
||||
assertThat(latest).isEmpty()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wifi network updates to active - carrier merged connection removed`() =
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
fakeWifiEventFlow.value = validCarrierMergedEvent(subId = 1)
|
||||
|
||||
assertThat(latest).hasSize(1)
|
||||
|
||||
fakeWifiEventFlow.value =
|
||||
FakeWifiEventModel.Wifi(
|
||||
level = 1,
|
||||
activity = 0,
|
||||
ssid = null,
|
||||
validated = true,
|
||||
)
|
||||
|
||||
assertThat(latest).isEmpty()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile sub updates to carrier merged - only one connection`() =
|
||||
testScope.runTest {
|
||||
var latestSubsList: List<SubscriptionModel>? = null
|
||||
var connections: List<DemoMobileConnectionRepository>? = null
|
||||
val job =
|
||||
underTest.subscriptions
|
||||
.onEach { latestSubsList = it }
|
||||
.onEach { infos ->
|
||||
connections =
|
||||
infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
fakeNetworkEventFlow.value = validMobileEvent(subId = 3, level = 2)
|
||||
assertThat(latestSubsList).hasSize(1)
|
||||
|
||||
val carrierMergedEvent = validCarrierMergedEvent(subId = 3, level = 1)
|
||||
fakeWifiEventFlow.value = carrierMergedEvent
|
||||
assertThat(latestSubsList).hasSize(1)
|
||||
val connection = connections!!.find { it.subId == 3 }!!
|
||||
assertCarrierMergedConnection(connection, carrierMergedEvent)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mobile sub updates to carrier merged then back - has old mobile data`() =
|
||||
testScope.runTest {
|
||||
var latestSubsList: List<SubscriptionModel>? = null
|
||||
var connections: List<DemoMobileConnectionRepository>? = null
|
||||
val job =
|
||||
underTest.subscriptions
|
||||
.onEach { latestSubsList = it }
|
||||
.onEach { infos ->
|
||||
connections =
|
||||
infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
val mobileEvent = validMobileEvent(subId = 3, level = 2)
|
||||
fakeNetworkEventFlow.value = mobileEvent
|
||||
assertThat(latestSubsList).hasSize(1)
|
||||
|
||||
val carrierMergedEvent = validCarrierMergedEvent(subId = 3, level = 1)
|
||||
fakeWifiEventFlow.value = carrierMergedEvent
|
||||
assertThat(latestSubsList).hasSize(1)
|
||||
var connection = connections!!.find { it.subId == 3 }!!
|
||||
assertCarrierMergedConnection(connection, carrierMergedEvent)
|
||||
|
||||
// WHEN the carrier merged is removed
|
||||
fakeWifiEventFlow.value =
|
||||
FakeWifiEventModel.Wifi(
|
||||
level = 4,
|
||||
activity = 0,
|
||||
ssid = null,
|
||||
validated = true,
|
||||
)
|
||||
|
||||
// THEN the subId=3 connection goes back to the mobile information
|
||||
connection = connections!!.find { it.subId == 3 }!!
|
||||
assertConnection(connection, mobileEvent)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/** Regression test for b/261706421 */
|
||||
@Test
|
||||
fun `multiple connections - remove all - does not throw`() =
|
||||
@@ -289,6 +471,51 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `demo connection - two connections - update carrier merged - no affect on first`() =
|
||||
testScope.runTest {
|
||||
var currentEvent1 = validMobileEvent(subId = 1)
|
||||
var connection1: DemoMobileConnectionRepository? = null
|
||||
var currentEvent2 = validCarrierMergedEvent(subId = 2)
|
||||
var connection2: DemoMobileConnectionRepository? = null
|
||||
var connections: List<DemoMobileConnectionRepository>? = null
|
||||
val job =
|
||||
underTest.subscriptions
|
||||
.onEach { infos ->
|
||||
connections =
|
||||
infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
fakeNetworkEventFlow.value = currentEvent1
|
||||
fakeWifiEventFlow.value = currentEvent2
|
||||
assertThat(connections).hasSize(2)
|
||||
connections!!.forEach {
|
||||
when (it.subId) {
|
||||
1 -> connection1 = it
|
||||
2 -> connection2 = it
|
||||
else -> Assert.fail("Unexpected subscription")
|
||||
}
|
||||
}
|
||||
|
||||
assertConnection(connection1!!, currentEvent1)
|
||||
assertCarrierMergedConnection(connection2!!, currentEvent2)
|
||||
|
||||
// WHEN the event changes for connection 2, it updates, and connection 1 stays the same
|
||||
currentEvent2 = validCarrierMergedEvent(subId = 2, level = 4)
|
||||
fakeWifiEventFlow.value = currentEvent2
|
||||
assertConnection(connection1!!, currentEvent1)
|
||||
assertCarrierMergedConnection(connection2!!, currentEvent2)
|
||||
|
||||
// and vice versa
|
||||
currentEvent1 = validMobileEvent(subId = 1, inflateStrength = true)
|
||||
fakeNetworkEventFlow.value = currentEvent1
|
||||
assertConnection(connection1!!, currentEvent1)
|
||||
assertCarrierMergedConnection(connection2!!, currentEvent2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun assertConnection(
|
||||
conn: DemoMobileConnectionRepository,
|
||||
model: FakeNetworkEventModel
|
||||
@@ -315,6 +542,21 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertCarrierMergedConnection(
|
||||
conn: DemoMobileConnectionRepository,
|
||||
model: FakeWifiEventModel.CarrierMerged,
|
||||
) {
|
||||
val connectionInfo: MobileConnectionModel = conn.connectionInfo.value
|
||||
assertThat(conn.subId).isEqualTo(model.subscriptionId)
|
||||
assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
|
||||
assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
|
||||
assertThat(connectionInfo.carrierNetworkChangeActive).isEqualTo(false)
|
||||
assertThat(connectionInfo.isRoaming).isEqualTo(false)
|
||||
assertThat(connectionInfo.isEmergencyOnly).isFalse()
|
||||
assertThat(connectionInfo.isGsm).isFalse()
|
||||
assertThat(connectionInfo.dataConnectionState).isEqualTo(DataConnectionState.Connected)
|
||||
}
|
||||
}
|
||||
|
||||
/** Convenience to create a valid fake network event with minimal params */
|
||||
@@ -339,3 +581,14 @@ fun validMobileEvent(
|
||||
roaming = roaming,
|
||||
name = "demo name",
|
||||
)
|
||||
|
||||
fun validCarrierMergedEvent(
|
||||
subId: Int = 1,
|
||||
level: Int = 1,
|
||||
numberOfLevels: Int = 4,
|
||||
): FakeWifiEventModel.CarrierMerged =
|
||||
FakeWifiEventModel.CarrierMerged(
|
||||
subscriptionId = subId,
|
||||
level = level,
|
||||
numberOfLevels = numberOfLevels,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.mobile.data.repository.prod
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
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.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var underTest: CarrierMergedConnectionRepository
|
||||
|
||||
private lateinit var wifiRepository: FakeWifiRepository
|
||||
@Mock private lateinit var logger: TableLogBuffer
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
wifiRepository = FakeWifiRepository()
|
||||
|
||||
underTest =
|
||||
CarrierMergedConnectionRepository(
|
||||
SUB_ID,
|
||||
logger,
|
||||
NetworkNameModel.Default("name"),
|
||||
testScope.backgroundScope,
|
||||
wifiRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_inactiveWifi_isDefault() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectionModel())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_activeWifi_isDefault() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = NET_ID, level = 1))
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectionModel())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_carrierMergedWifi_isValidAndFieldsComeFromWifiNetwork() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setIsWifiEnabled(true)
|
||||
wifiRepository.setIsWifiDefault(true)
|
||||
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NET_ID,
|
||||
subscriptionId = SUB_ID,
|
||||
level = 3,
|
||||
)
|
||||
)
|
||||
|
||||
val expected =
|
||||
MobileConnectionModel(
|
||||
primaryLevel = 3,
|
||||
cdmaLevel = 3,
|
||||
dataConnectionState = DataConnectionState.Connected,
|
||||
dataActivityDirection =
|
||||
DataActivityModel(
|
||||
hasActivityIn = false,
|
||||
hasActivityOut = false,
|
||||
),
|
||||
resolvedNetworkType = ResolvedNetworkType.CarrierMergedNetworkType,
|
||||
isRoaming = false,
|
||||
isEmergencyOnly = false,
|
||||
operatorAlphaShort = null,
|
||||
isInService = true,
|
||||
isGsm = false,
|
||||
carrierNetworkChangeActive = false,
|
||||
)
|
||||
assertThat(latest).isEqualTo(expected)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_carrierMergedWifi_wrongSubId_isDefault() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NET_ID,
|
||||
subscriptionId = SUB_ID + 10,
|
||||
level = 3,
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectionModel())
|
||||
assertThat(latest!!.primaryLevel).isNotEqualTo(3)
|
||||
assertThat(latest!!.resolvedNetworkType)
|
||||
.isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
// This scenario likely isn't possible, but write a test for it anyway
|
||||
@Test
|
||||
fun connectionInfo_carrierMergedButNotEnabled_isDefault() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NET_ID,
|
||||
subscriptionId = SUB_ID,
|
||||
level = 3,
|
||||
)
|
||||
)
|
||||
wifiRepository.setIsWifiEnabled(false)
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectionModel())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
// This scenario likely isn't possible, but write a test for it anyway
|
||||
@Test
|
||||
fun connectionInfo_carrierMergedButWifiNotDefault_isDefault() =
|
||||
testScope.runTest {
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NET_ID,
|
||||
subscriptionId = SUB_ID,
|
||||
level = 3,
|
||||
)
|
||||
)
|
||||
wifiRepository.setIsWifiDefault(false)
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectionModel())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun numberOfLevels_comesFromCarrierMerged() =
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NET_ID,
|
||||
subscriptionId = SUB_ID,
|
||||
level = 1,
|
||||
numberOfLevels = 6,
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(latest).isEqualTo(6)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dataEnabled_matchesWifiEnabled() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setIsWifiEnabled(true)
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
wifiRepository.setIsWifiEnabled(false)
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cdmaRoaming_alwaysFalse() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 123
|
||||
const val NET_ID = 456
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.mobile.data.repository.prod
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
/**
|
||||
* This repo acts as a dispatcher to either the `typical` or `carrier merged` versions of the
|
||||
* repository interface it's switching on. These tests just need to verify that the entire interface
|
||||
* properly switches over when the value of `isCarrierMerged` changes.
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var underTest: FullMobileConnectionRepository
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val tableLogBuffer = mock<TableLogBuffer>()
|
||||
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
|
||||
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
|
||||
|
||||
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
|
||||
private val globalMobileDataSettingChangedEvent: Flow<Unit>
|
||||
get() = connectionsRepo.globalMobileDataSettingChangedEvent
|
||||
|
||||
private lateinit var mobileRepo: FakeMobileConnectionRepository
|
||||
private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogBuffer)
|
||||
|
||||
mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
|
||||
carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
|
||||
|
||||
whenever(
|
||||
mobileFactory.build(
|
||||
eq(SUB_ID),
|
||||
any(),
|
||||
eq(DEFAULT_NAME),
|
||||
eq(SEP),
|
||||
eq(globalMobileDataSettingChangedEvent),
|
||||
)
|
||||
)
|
||||
.thenReturn(mobileRepo)
|
||||
whenever(carrierMergedFactory.build(eq(SUB_ID), any(), eq(DEFAULT_NAME)))
|
||||
.thenReturn(carrierMergedRepo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startingIsCarrierMerged_usesCarrierMergedInitially() =
|
||||
testScope.runTest {
|
||||
val carrierMergedConnectionInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Carrier Merged Operator",
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(carrierMergedConnectionInfo)
|
||||
|
||||
initializeRepo(startingIsCarrierMerged = true)
|
||||
|
||||
assertThat(underTest.activeRepo.value).isEqualTo(carrierMergedRepo)
|
||||
assertThat(underTest.connectionInfo.value).isEqualTo(carrierMergedConnectionInfo)
|
||||
verify(mobileFactory, never())
|
||||
.build(
|
||||
SUB_ID,
|
||||
tableLogBuffer,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startingNotCarrierMerged_usesTypicalInitially() =
|
||||
testScope.runTest {
|
||||
val mobileConnectionInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Typical Operator",
|
||||
)
|
||||
mobileRepo.setConnectionInfo(mobileConnectionInfo)
|
||||
|
||||
initializeRepo(startingIsCarrierMerged = false)
|
||||
|
||||
assertThat(underTest.activeRepo.value).isEqualTo(mobileRepo)
|
||||
assertThat(underTest.connectionInfo.value).isEqualTo(mobileConnectionInfo)
|
||||
verify(carrierMergedFactory, never()).build(SUB_ID, tableLogBuffer, DEFAULT_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun activeRepo_matchesIsCarrierMerged() =
|
||||
testScope.runTest {
|
||||
initializeRepo(startingIsCarrierMerged = false)
|
||||
var latest: MobileConnectionRepository? = null
|
||||
val job = underTest.activeRepo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
underTest.setIsCarrierMerged(true)
|
||||
|
||||
assertThat(latest).isEqualTo(carrierMergedRepo)
|
||||
|
||||
underTest.setIsCarrierMerged(false)
|
||||
|
||||
assertThat(latest).isEqualTo(mobileRepo)
|
||||
|
||||
underTest.setIsCarrierMerged(true)
|
||||
|
||||
assertThat(latest).isEqualTo(carrierMergedRepo)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_getsUpdatesFromRepo_carrierMerged() =
|
||||
testScope.runTest {
|
||||
initializeRepo(startingIsCarrierMerged = false)
|
||||
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
underTest.setIsCarrierMerged(true)
|
||||
|
||||
val info1 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Carrier Merged Operator",
|
||||
primaryLevel = 1,
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(info1)
|
||||
|
||||
assertThat(latest).isEqualTo(info1)
|
||||
|
||||
val info2 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Carrier Merged Operator #2",
|
||||
primaryLevel = 2,
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(info2)
|
||||
|
||||
assertThat(latest).isEqualTo(info2)
|
||||
|
||||
val info3 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Carrier Merged Operator #3",
|
||||
primaryLevel = 3,
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(info3)
|
||||
|
||||
assertThat(latest).isEqualTo(info3)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_getsUpdatesFromRepo_mobile() =
|
||||
testScope.runTest {
|
||||
initializeRepo(startingIsCarrierMerged = false)
|
||||
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
underTest.setIsCarrierMerged(false)
|
||||
|
||||
val info1 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Typical Merged Operator",
|
||||
primaryLevel = 1,
|
||||
)
|
||||
mobileRepo.setConnectionInfo(info1)
|
||||
|
||||
assertThat(latest).isEqualTo(info1)
|
||||
|
||||
val info2 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Typical Merged Operator #2",
|
||||
primaryLevel = 2,
|
||||
)
|
||||
mobileRepo.setConnectionInfo(info2)
|
||||
|
||||
assertThat(latest).isEqualTo(info2)
|
||||
|
||||
val info3 =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Typical Merged Operator #3",
|
||||
primaryLevel = 3,
|
||||
)
|
||||
mobileRepo.setConnectionInfo(info3)
|
||||
|
||||
assertThat(latest).isEqualTo(info3)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionInfo_updatesWhenCarrierMergedUpdates() =
|
||||
testScope.runTest {
|
||||
initializeRepo(startingIsCarrierMerged = false)
|
||||
|
||||
var latest: MobileConnectionModel? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val carrierMergedInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Carrier Merged Operator",
|
||||
primaryLevel = 4,
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(carrierMergedInfo)
|
||||
|
||||
val mobileInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "Typical Operator",
|
||||
primaryLevel = 2,
|
||||
)
|
||||
mobileRepo.setConnectionInfo(mobileInfo)
|
||||
|
||||
// Start with the mobile info
|
||||
assertThat(latest).isEqualTo(mobileInfo)
|
||||
|
||||
// WHEN isCarrierMerged is set to true
|
||||
underTest.setIsCarrierMerged(true)
|
||||
|
||||
// THEN the carrier merged info is used
|
||||
assertThat(latest).isEqualTo(carrierMergedInfo)
|
||||
|
||||
val newCarrierMergedInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "New CM Operator",
|
||||
primaryLevel = 0,
|
||||
)
|
||||
carrierMergedRepo.setConnectionInfo(newCarrierMergedInfo)
|
||||
|
||||
assertThat(latest).isEqualTo(newCarrierMergedInfo)
|
||||
|
||||
// WHEN isCarrierMerged is set to false
|
||||
underTest.setIsCarrierMerged(false)
|
||||
|
||||
// THEN the typical info is used
|
||||
assertThat(latest).isEqualTo(mobileInfo)
|
||||
|
||||
val newMobileInfo =
|
||||
MobileConnectionModel(
|
||||
operatorAlphaShort = "New Mobile Operator",
|
||||
primaryLevel = 3,
|
||||
)
|
||||
mobileRepo.setConnectionInfo(newMobileInfo)
|
||||
|
||||
assertThat(latest).isEqualTo(newMobileInfo)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `factory - reuses log buffers for same connection`() =
|
||||
testScope.runTest {
|
||||
val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
|
||||
|
||||
val factory =
|
||||
FullMobileConnectionRepository.Factory(
|
||||
scope = testScope.backgroundScope,
|
||||
realLoggerFactory,
|
||||
mobileFactory,
|
||||
carrierMergedFactory,
|
||||
)
|
||||
|
||||
// Create two connections for the same subId. Similar to if the connection appeared
|
||||
// and disappeared from the connectionFactory's perspective
|
||||
val connection1 =
|
||||
factory.build(
|
||||
SUB_ID,
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
val connection1Repeat =
|
||||
factory.build(
|
||||
SUB_ID,
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
assertThat(connection1.tableLogBuffer)
|
||||
.isSameInstanceAs(connection1Repeat.tableLogBuffer)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `factory - reuses log buffers for same sub ID even if carrier merged`() =
|
||||
testScope.runTest {
|
||||
val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
|
||||
|
||||
val factory =
|
||||
FullMobileConnectionRepository.Factory(
|
||||
scope = testScope.backgroundScope,
|
||||
realLoggerFactory,
|
||||
mobileFactory,
|
||||
carrierMergedFactory,
|
||||
)
|
||||
|
||||
val connection1 =
|
||||
factory.build(
|
||||
SUB_ID,
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
// WHEN a connection with the same sub ID but carrierMerged = true is created
|
||||
val connection1Repeat =
|
||||
factory.build(
|
||||
SUB_ID,
|
||||
startingIsCarrierMerged = true,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
// THEN the same table is re-used
|
||||
assertThat(connection1.tableLogBuffer)
|
||||
.isSameInstanceAs(connection1Repeat.tableLogBuffer)
|
||||
}
|
||||
|
||||
// TODO(b/238425913): Verify that the logging switches correctly (once the carrier merged repo
|
||||
// implements logging).
|
||||
|
||||
private fun initializeRepo(startingIsCarrierMerged: Boolean) {
|
||||
underTest =
|
||||
FullMobileConnectionRepository(
|
||||
SUB_ID,
|
||||
startingIsCarrierMerged,
|
||||
tableLogBuffer,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
testScope.backgroundScope,
|
||||
mobileFactory,
|
||||
carrierMergedFactory,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 42
|
||||
private val DEFAULT_NAME = NetworkNameModel.Default("default name")
|
||||
private const val SEP = "-"
|
||||
}
|
||||
}
|
||||
@@ -37,17 +37,18 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
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.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -74,6 +75,9 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileConnectionsRepositoryImpl
|
||||
|
||||
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
|
||||
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
|
||||
private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
|
||||
private lateinit var wifiRepository: FakeWifiRepository
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var subscriptionManager: SubscriptionManager
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@@ -100,6 +104,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
mock<TableLogBuffer>()
|
||||
}
|
||||
|
||||
wifiRepository = FakeWifiRepository()
|
||||
|
||||
connectionFactory =
|
||||
MobileConnectionRepositoryImpl.Factory(
|
||||
fakeBroadcastDispatcher,
|
||||
@@ -110,7 +116,18 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
logger = logger,
|
||||
mobileMappingsProxy = mobileMappings,
|
||||
scope = scope,
|
||||
)
|
||||
carrierMergedFactory =
|
||||
CarrierMergedConnectionRepository.Factory(
|
||||
scope,
|
||||
wifiRepository,
|
||||
)
|
||||
fullConnectionFactory =
|
||||
FullMobileConnectionRepository.Factory(
|
||||
scope = scope,
|
||||
logFactory = logBufferFactory,
|
||||
mobileRepoFactory = connectionFactory,
|
||||
carrierMergedRepoFactory = carrierMergedFactory,
|
||||
)
|
||||
|
||||
underTest =
|
||||
@@ -125,7 +142,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
connectionFactory,
|
||||
wifiRepository,
|
||||
fullConnectionFactory,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -179,6 +197,40 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscriptions_carrierMergedOnly_listHasCarrierMerged() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
assertThat(latest).isEqualTo(listOf(MODEL_CM))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscriptions_carrierMergedAndOther_listHasBothWithCarrierMergedLast() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
|
||||
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2, MODEL_CM))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testActiveDataSubscriptionId_initialValueIsInvalidId() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
@@ -218,6 +270,96 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConnectionRepository_carrierMergedSubId_isCached() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.subscriptions.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
val repo1 = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
val repo2 = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
|
||||
assertThat(repo1).isSameInstanceAs(repo2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.subscriptions.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1, SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
|
||||
assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
|
||||
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.subscriptions.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1, SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
|
||||
assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
|
||||
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
|
||||
|
||||
// WHEN the wifi network updates to be not carrier merged
|
||||
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 4, level = 1))
|
||||
|
||||
// THEN the repos update
|
||||
val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
|
||||
assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
|
||||
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.subscriptions.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1, SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
|
||||
assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
|
||||
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
|
||||
|
||||
// WHEN the wifi network updates to be carrier merged
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
|
||||
// THEN the repos update
|
||||
val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
|
||||
assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
|
||||
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConnectionCache_clearsInvalidSubscriptions() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
@@ -244,6 +386,34 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.subscriptions.launchIn(this)
|
||||
|
||||
wifiRepository.setWifiNetwork(WIFI_NETWORK_CM)
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
// Get repos to trigger caching
|
||||
val repo1 = underTest.getRepoForSubId(SUB_1_ID)
|
||||
val repo2 = underTest.getRepoForSubId(SUB_2_ID)
|
||||
val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
|
||||
|
||||
assertThat(underTest.getSubIdRepoCache())
|
||||
.containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
|
||||
|
||||
// SUB_2 and SUB_CM disappear
|
||||
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
|
||||
.thenReturn(listOf(SUB_1))
|
||||
getSubscriptionCallback().onSubscriptionsChanged()
|
||||
|
||||
assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/** Regression test for b/261706421 */
|
||||
@Test
|
||||
fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
|
||||
@@ -295,59 +465,19 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
underTest.getRepoForSubId(SUB_1_ID)
|
||||
verify(logBufferFactory)
|
||||
.getOrCreate(
|
||||
eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_1_ID)),
|
||||
eq(tableBufferLogName(SUB_1_ID)),
|
||||
anyInt(),
|
||||
)
|
||||
underTest.getRepoForSubId(SUB_2_ID)
|
||||
verify(logBufferFactory)
|
||||
.getOrCreate(
|
||||
eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_2_ID)),
|
||||
eq(tableBufferLogName(SUB_2_ID)),
|
||||
anyInt(),
|
||||
)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `connection repository factory - reuses log buffers for same connection`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
|
||||
|
||||
connectionFactory =
|
||||
MobileConnectionRepositoryImpl.Factory(
|
||||
fakeBroadcastDispatcher,
|
||||
context = context,
|
||||
telephonyManager = telephonyManager,
|
||||
bgDispatcher = IMMEDIATE,
|
||||
globalSettings = globalSettings,
|
||||
logger = logger,
|
||||
mobileMappingsProxy = mobileMappings,
|
||||
scope = scope,
|
||||
logFactory = realLoggerFactory,
|
||||
)
|
||||
|
||||
// Create two connections for the same subId. Similar to if the connection appeared
|
||||
// and disappeared from the connectionFactory's perspective
|
||||
val connection1 =
|
||||
connectionFactory.build(
|
||||
1,
|
||||
NetworkNameModel.Default("default_name"),
|
||||
"-",
|
||||
underTest.globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
val connection1_repeat =
|
||||
connectionFactory.build(
|
||||
1,
|
||||
NetworkNameModel.Default("default_name"),
|
||||
"-",
|
||||
underTest.globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
assertThat(connection1.tableLogBuffer)
|
||||
.isSameInstanceAs(connection1_repeat.tableLogBuffer)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mobileConnectivity_default() {
|
||||
assertThat(underTest.defaultMobileNetworkConnectivity.value)
|
||||
@@ -461,7 +591,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
connectionFactory,
|
||||
wifiRepository,
|
||||
fullConnectionFactory,
|
||||
)
|
||||
|
||||
var latest: MobileMappings.Config? = null
|
||||
@@ -571,5 +702,16 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private const val NET_ID = 123
|
||||
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
|
||||
|
||||
private const val SUB_CM_ID = 5
|
||||
private val SUB_CM =
|
||||
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
|
||||
private val MODEL_CM = SubscriptionModel(subscriptionId = SUB_CM_ID)
|
||||
private val WIFI_NETWORK_CM =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = 3,
|
||||
subscriptionId = SUB_CM_ID,
|
||||
level = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
|
||||
@@ -270,6 +271,23 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iconGroup_carrierMerged_usesOverride() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
resolvedNetworkType = CarrierMergedNetworkType,
|
||||
),
|
||||
)
|
||||
|
||||
var latest: MobileIconGroup? = null
|
||||
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEqualTo(CarrierMergedNetworkType.iconGroupOverride)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alwaysShowDataRatIcon_matchesParent() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.model
|
||||
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableRowLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MIN_VALID_LEVEL
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
@@ -44,8 +45,52 @@ class WifiNetworkModelTest : SysuiTestCase() {
|
||||
WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun carrierMerged_invalidSubId_exceptionThrown() {
|
||||
WifiNetworkModel.CarrierMerged(NETWORK_ID, INVALID_SUBSCRIPTION_ID, 1)
|
||||
}
|
||||
|
||||
// Non-exhaustive logDiffs test -- just want to make sure the logging logic isn't totally broken
|
||||
|
||||
@Test
|
||||
fun logDiffs_carrierMergedToInactive_resetsAllFields() {
|
||||
val logger = TestLogger()
|
||||
val prevVal =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = 5,
|
||||
subscriptionId = 3,
|
||||
level = 1,
|
||||
)
|
||||
|
||||
WifiNetworkModel.Inactive.logDiffs(prevVal, logger)
|
||||
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
|
||||
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
|
||||
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
|
||||
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun logDiffs_inactiveToCarrierMerged_logsAllFields() {
|
||||
val logger = TestLogger()
|
||||
val carrierMerged =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = 6,
|
||||
subscriptionId = 3,
|
||||
level = 2,
|
||||
)
|
||||
|
||||
carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
|
||||
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
|
||||
assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
|
||||
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
|
||||
assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
|
||||
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun logDiffs_inactiveToActive_logsAllActiveFields() {
|
||||
val logger = TestLogger()
|
||||
@@ -95,8 +140,14 @@ class WifiNetworkModelTest : SysuiTestCase() {
|
||||
level = 3,
|
||||
ssid = "Test SSID"
|
||||
)
|
||||
val prevVal =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = 5,
|
||||
subscriptionId = 3,
|
||||
level = 1,
|
||||
)
|
||||
|
||||
activeNetwork.logDiffs(prevVal = WifiNetworkModel.CarrierMerged, logger)
|
||||
activeNetwork.logDiffs(prevVal, logger)
|
||||
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
|
||||
@@ -105,7 +156,7 @@ class WifiNetworkModelTest : SysuiTestCase() {
|
||||
assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
|
||||
}
|
||||
@Test
|
||||
fun logDiffs_activeToCarrierMerged_resetsAllActiveFields() {
|
||||
fun logDiffs_activeToCarrierMerged_logsAllFields() {
|
||||
val logger = TestLogger()
|
||||
val activeNetwork =
|
||||
WifiNetworkModel.Active(
|
||||
@@ -114,13 +165,20 @@ class WifiNetworkModelTest : SysuiTestCase() {
|
||||
level = 3,
|
||||
ssid = "Test SSID"
|
||||
)
|
||||
val carrierMerged =
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = 6,
|
||||
subscriptionId = 3,
|
||||
level = 2,
|
||||
)
|
||||
|
||||
WifiNetworkModel.CarrierMerged.logDiffs(prevVal = activeNetwork, logger)
|
||||
carrierMerged.logDiffs(prevVal = activeNetwork, logger)
|
||||
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
|
||||
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
|
||||
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
|
||||
assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
|
||||
assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
|
||||
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
|
||||
assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
|
||||
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.net.vcn.VcnTransportInfo
|
||||
import android.net.wifi.WifiInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiManager.TrafficStateCallback
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
@@ -340,7 +341,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
whenever(this.isCarrierMerged).thenReturn(true)
|
||||
}
|
||||
@@ -352,6 +352,67 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
whenever(this.isCarrierMerged).thenReturn(true)
|
||||
whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(
|
||||
NETWORK,
|
||||
createWifiNetworkCapabilities(wifiInfo),
|
||||
)
|
||||
|
||||
assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_isCarrierMerged_getsCorrectValues() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val rssi = -57
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
whenever(this.isCarrierMerged).thenReturn(true)
|
||||
whenever(this.rssi).thenReturn(rssi)
|
||||
whenever(this.subscriptionId).thenReturn(567)
|
||||
}
|
||||
|
||||
whenever(wifiManager.calculateSignalLevel(rssi)).thenReturn(2)
|
||||
whenever(wifiManager.maxSignalLevel).thenReturn(5)
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(
|
||||
NETWORK,
|
||||
createWifiNetworkCapabilities(wifiInfo),
|
||||
)
|
||||
|
||||
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
|
||||
val latestCarrierMerged = latest as WifiNetworkModel.CarrierMerged
|
||||
assertThat(latestCarrierMerged.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestCarrierMerged.subscriptionId).isEqualTo(567)
|
||||
assertThat(latestCarrierMerged.level).isEqualTo(2)
|
||||
// numberOfLevels = maxSignalLevel + 1
|
||||
assertThat(latestCarrierMerged.numberOfLevels).isEqualTo(6)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_notValidated_networkNotValidated() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
|
||||
@@ -84,7 +84,9 @@ class WifiInteractorImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun ssid_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
|
||||
wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
|
||||
wifiRepository.setWifiNetwork(
|
||||
WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
|
||||
)
|
||||
|
||||
var latest: String? = "default"
|
||||
val job = underTest
|
||||
|
||||
@@ -206,7 +206,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
|
||||
// Enabled = false => no networks shown
|
||||
TestCase(
|
||||
enabled = false,
|
||||
network = WifiNetworkModel.CarrierMerged,
|
||||
network =
|
||||
WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
|
||||
expected = null,
|
||||
),
|
||||
TestCase(
|
||||
@@ -228,7 +229,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
|
||||
// forceHidden = true => no networks shown
|
||||
TestCase(
|
||||
forceHidden = true,
|
||||
network = WifiNetworkModel.CarrierMerged,
|
||||
network =
|
||||
WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
|
||||
expected = null,
|
||||
),
|
||||
TestCase(
|
||||
@@ -369,7 +371,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
|
||||
|
||||
// network = CarrierMerged => not shown
|
||||
TestCase(
|
||||
network = WifiNetworkModel.CarrierMerged,
|
||||
network =
|
||||
WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
|
||||
expected = null,
|
||||
),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user