Merge changes Id57cc404,Ieaf30071,Idb2b182c,I5de259d8,I5d7762b7 into tm-qpr-dev
* changes: [Sb refactor] Fix missed telephony callback events [Sb refactor] Handle telephony display info with only an override [Sb refactor][cleanup] Move testing to testScope [Sb refactor][cleanup] remove spaces from tests [Sb refactor] Filter out -1 values for ERI
This commit is contained in:
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.telephony.CellSignalStrength
|
||||
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
|
||||
import android.telephony.CellSignalStrengthCdma
|
||||
import android.telephony.ServiceState
|
||||
@@ -28,12 +27,12 @@ import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.ERI_OFF
|
||||
import android.telephony.TelephonyManager.ERI_FLASH
|
||||
import android.telephony.TelephonyManager.ERI_ON
|
||||
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
@@ -59,16 +58,14 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.scan
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
@@ -100,8 +97,6 @@ class MobileConnectionRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private val telephonyCallbackEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
|
||||
|
||||
/**
|
||||
* This flow defines the single shared connection to system_server via TelephonyCallback. Any
|
||||
* new callback should be added to this listener and funneled through callbackEvents via a data
|
||||
@@ -109,9 +104,15 @@ class MobileConnectionRepositoryImpl(
|
||||
*
|
||||
* The reason we need to do this is because TelephonyManager limits the number of registered
|
||||
* listeners per-process, so we don't want to create a new listener for every callback.
|
||||
*
|
||||
* A note on the design for back pressure here: We use the [coalesce] operator here to change
|
||||
* the backpressure strategy to store exactly the last callback event of _each type_ here, as
|
||||
* opposed to the default strategy which is to drop the oldest event (regardless of type). This
|
||||
* means that we should never miss any single event as long as the flow has been started.
|
||||
*/
|
||||
private val callbackEvents: SharedFlow<CallbackEvent> =
|
||||
conflatedCallbackFlow {
|
||||
private val callbackEvents: StateFlow<TelephonyCallbackState> = run {
|
||||
val initial = TelephonyCallbackState()
|
||||
callbackFlow {
|
||||
val callback =
|
||||
object :
|
||||
TelephonyCallback(),
|
||||
@@ -165,48 +166,50 @@ class MobileConnectionRepositoryImpl(
|
||||
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
|
||||
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
|
||||
}
|
||||
.shareIn(scope, SharingStarted.WhileSubscribed())
|
||||
.scan(initial = initial) { state, event -> state.applyEvent(event) }
|
||||
.stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
override val isEmergencyOnly =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnServiceStateChanged>()
|
||||
.mapNotNull { it.onServiceStateChanged }
|
||||
.map { it.serviceState.isEmergencyOnly }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val isRoaming =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnServiceStateChanged>()
|
||||
.mapNotNull { it.onServiceStateChanged }
|
||||
.map { it.serviceState.roaming }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val operatorAlphaShort =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnServiceStateChanged>()
|
||||
.mapNotNull { it.onServiceStateChanged }
|
||||
.map { it.serviceState.operatorAlphaShort }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
override val isInService =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnServiceStateChanged>()
|
||||
.mapNotNull { it.onServiceStateChanged }
|
||||
.map { Utils.isInService(it.serviceState) }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val isGsm =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnSignalStrengthChanged>()
|
||||
.mapNotNull { it.onSignalStrengthChanged }
|
||||
.map { it.signalStrength.isGsm }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val cdmaLevel =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnSignalStrengthChanged>()
|
||||
.mapNotNull { it.onSignalStrengthChanged }
|
||||
.map {
|
||||
it.signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java).let {
|
||||
strengths ->
|
||||
if (strengths.isNotEmpty()) {
|
||||
strengths[0].level
|
||||
} else {
|
||||
CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
|
||||
SIGNAL_STRENGTH_NONE_OR_UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,19 +217,19 @@ class MobileConnectionRepositoryImpl(
|
||||
|
||||
override val primaryLevel =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnSignalStrengthChanged>()
|
||||
.mapNotNull { it.onSignalStrengthChanged }
|
||||
.map { it.signalStrength.level }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
|
||||
|
||||
override val dataConnectionState =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnDataConnectionStateChanged>()
|
||||
.mapNotNull { it.onDataConnectionStateChanged }
|
||||
.map { it.dataState.toDataConnectionType() }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), Disconnected)
|
||||
|
||||
override val dataActivityDirection =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnDataActivity>()
|
||||
.mapNotNull { it.onDataActivity }
|
||||
.map { it.direction.toMobileDataActivityModel() }
|
||||
.stateIn(
|
||||
scope,
|
||||
@@ -236,28 +239,26 @@ class MobileConnectionRepositoryImpl(
|
||||
|
||||
override val carrierNetworkChangeActive =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnCarrierNetworkChange>()
|
||||
.mapNotNull { it.onCarrierNetworkChange }
|
||||
.map { it.active }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val resolvedNetworkType =
|
||||
callbackEvents
|
||||
.filterIsInstance<CallbackEvent.OnDisplayInfoChanged>()
|
||||
.mapNotNull { it.onDisplayInfoChanged }
|
||||
.map {
|
||||
if (it.telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) {
|
||||
UnknownNetworkType
|
||||
} else if (
|
||||
it.telephonyDisplayInfo.overrideNetworkType == OVERRIDE_NETWORK_TYPE_NONE
|
||||
) {
|
||||
DefaultNetworkType(
|
||||
mobileMappingsProxy.toIconKey(it.telephonyDisplayInfo.networkType)
|
||||
)
|
||||
} else {
|
||||
if (it.telephonyDisplayInfo.overrideNetworkType != OVERRIDE_NETWORK_TYPE_NONE) {
|
||||
OverrideNetworkType(
|
||||
mobileMappingsProxy.toIconKeyOverride(
|
||||
it.telephonyDisplayInfo.overrideNetworkType
|
||||
)
|
||||
)
|
||||
} else if (it.telephonyDisplayInfo.networkType != NETWORK_TYPE_UNKNOWN) {
|
||||
DefaultNetworkType(
|
||||
mobileMappingsProxy.toIconKey(it.telephonyDisplayInfo.networkType)
|
||||
)
|
||||
} else {
|
||||
UnknownNetworkType
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
|
||||
@@ -282,7 +283,10 @@ class MobileConnectionRepositoryImpl(
|
||||
|
||||
override val cdmaRoaming: StateFlow<Boolean> =
|
||||
telephonyPollingEvent
|
||||
.mapLatest { telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF }
|
||||
.mapLatest {
|
||||
val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
|
||||
cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val networkName: StateFlow<NetworkNameModel> =
|
||||
@@ -300,7 +304,8 @@ class MobileConnectionRepositoryImpl(
|
||||
override val dataEnabled = run {
|
||||
val initial = telephonyManager.isDataConnectionAllowed
|
||||
callbackEvents
|
||||
.mapNotNull { (it as? CallbackEvent.OnDataEnabledChanged)?.enabled }
|
||||
.mapNotNull { it.onDataEnabledChanged }
|
||||
.map { it.enabled }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
@@ -344,12 +349,41 @@ class MobileConnectionRepositoryImpl(
|
||||
* Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
|
||||
* shared flow and then split them back out into other flows.
|
||||
*/
|
||||
private sealed interface CallbackEvent {
|
||||
sealed interface CallbackEvent {
|
||||
data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
|
||||
data class OnDataActivity(val direction: Int) : CallbackEvent
|
||||
data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
|
||||
data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
|
||||
data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
|
||||
data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
|
||||
data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
|
||||
data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
|
||||
data class OnDataActivity(val direction: Int) : CallbackEvent
|
||||
data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
|
||||
data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
|
||||
data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple box type for 1-to-1 mapping of [CallbackEvent] to the batched event. Used in conjunction
|
||||
* with [scan] to make sure we don't drop important callbacks due to late subscribers
|
||||
*/
|
||||
data class TelephonyCallbackState(
|
||||
val onDataActivity: CallbackEvent.OnDataActivity? = null,
|
||||
val onCarrierNetworkChange: CallbackEvent.OnCarrierNetworkChange? = null,
|
||||
val onDataConnectionStateChanged: CallbackEvent.OnDataConnectionStateChanged? = null,
|
||||
val onDataEnabledChanged: CallbackEvent.OnDataEnabledChanged? = null,
|
||||
val onDisplayInfoChanged: CallbackEvent.OnDisplayInfoChanged? = null,
|
||||
val onServiceStateChanged: CallbackEvent.OnServiceStateChanged? = null,
|
||||
val onSignalStrengthChanged: CallbackEvent.OnSignalStrengthChanged? = null,
|
||||
) {
|
||||
fun applyEvent(event: CallbackEvent): TelephonyCallbackState {
|
||||
return when (event) {
|
||||
is CallbackEvent.OnCarrierNetworkChange -> copy(onCarrierNetworkChange = event)
|
||||
is CallbackEvent.OnDataActivity -> copy(onDataActivity = event)
|
||||
is CallbackEvent.OnDataConnectionStateChanged ->
|
||||
copy(onDataConnectionStateChanged = event)
|
||||
is CallbackEvent.OnDataEnabledChanged -> copy(onDataEnabledChanged = event)
|
||||
is CallbackEvent.OnDisplayInfoChanged -> copy(onDisplayInfoChanged = event)
|
||||
is CallbackEvent.OnServiceStateChanged -> {
|
||||
copy(onServiceStateChanged = event)
|
||||
}
|
||||
is CallbackEvent.OnSignalStrengthChanged -> copy(onSignalStrengthChanged = event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,16 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
|
||||
import android.content.Intent
|
||||
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
|
||||
import android.telephony.CellSignalStrengthCdma
|
||||
import android.telephony.NetworkRegistrationInfo
|
||||
import android.telephony.ServiceState
|
||||
import android.telephony.ServiceState.STATE_IN_SERVICE
|
||||
import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
|
||||
import android.telephony.SignalStrength
|
||||
import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyCallback.DataActivityListener
|
||||
import android.telephony.TelephonyCallback.ServiceStateListener
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
|
||||
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.DATA_ACTIVITY_DORMANT
|
||||
import android.telephony.TelephonyManager.DATA_ACTIVITY_IN
|
||||
@@ -68,6 +67,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrier
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
|
||||
@@ -75,14 +75,12 @@ import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
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.Mock
|
||||
@@ -99,7 +97,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
@Mock private lateinit var logger: MobileInputLogger
|
||||
@Mock private lateinit var tableLogger: TableLogBuffer
|
||||
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val systemUiCarrierConfig =
|
||||
SystemUiCarrierConfig(
|
||||
@@ -107,6 +104,9 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
createTestConfig(),
|
||||
)
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
@@ -124,21 +124,16 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
systemUiCarrierConfig,
|
||||
fakeBroadcastDispatcher,
|
||||
mobileMappings,
|
||||
IMMEDIATE,
|
||||
testDispatcher,
|
||||
logger,
|
||||
tableLogger,
|
||||
scope,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emergencyOnly() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isEmergencyOnly.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -154,18 +149,15 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun emergencyOnly_toggles() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isEmergencyOnly.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val callback = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
val serviceState = ServiceState()
|
||||
serviceState.isEmergencyOnly = true
|
||||
callback.onServiceStateChanged(serviceState)
|
||||
callback.onServiceStateChanged(ServiceState().also { it.isEmergencyOnly = true })
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
serviceState.isEmergencyOnly = false
|
||||
callback.onServiceStateChanged(serviceState)
|
||||
callback.onServiceStateChanged(ServiceState().also { it.isEmergencyOnly = false })
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
@@ -174,7 +166,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun cdmaLevelUpdates() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.cdmaLevel.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -194,7 +186,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun gsmLevelUpdates() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -214,7 +206,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun isGsm() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isGsm.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -234,7 +226,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_connected() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -249,7 +241,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_connecting() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -264,7 +256,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_disconnected() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -279,7 +271,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_disconnecting() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -294,7 +286,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_suspended() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -309,7 +301,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_handoverInProgress() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -324,7 +316,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_unknown() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -339,7 +331,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataConnectionState_invalid() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -354,7 +346,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun dataActivity() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: DataActivityModel? = null
|
||||
val job = underTest.dataActivityDirection.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -368,7 +360,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun carrierNetworkChange() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -382,7 +374,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_default() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -395,7 +387,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_unknown_hasCorrectKey() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -413,14 +405,19 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_updatesUsingDefault() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
|
||||
val overrideType = OVERRIDE_NETWORK_TYPE_NONE
|
||||
val type = NETWORK_TYPE_LTE
|
||||
val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
|
||||
val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
|
||||
val ti =
|
||||
mock<TelephonyDisplayInfo>().also {
|
||||
whenever(it.overrideNetworkType).thenReturn(overrideType)
|
||||
whenever(it.networkType).thenReturn(type)
|
||||
}
|
||||
callback.onDisplayInfoChanged(ti)
|
||||
|
||||
assertThat(latest).isEqualTo(expected)
|
||||
@@ -430,7 +427,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_updatesUsingOverride() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -449,17 +446,39 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkType_unknownNetworkWithOverride_usesOverrideKey() =
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
|
||||
val unknown = NETWORK_TYPE_UNKNOWN
|
||||
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
|
||||
val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
|
||||
val ti =
|
||||
mock<TelephonyDisplayInfo>().also {
|
||||
whenever(it.networkType).thenReturn(unknown)
|
||||
whenever(it.overrideNetworkType).thenReturn(type)
|
||||
}
|
||||
callback.onDisplayInfoChanged(ti)
|
||||
|
||||
assertThat(latest).isEqualTo(expected)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dataEnabled_initial_false() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
|
||||
|
||||
assertThat(underTest.dataEnabled.value).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `is data enabled - tracks telephony callback`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun isDataEnabled_tracksTelephonyCallback() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -479,7 +498,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun numberOfLevels_isDefault() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -489,51 +508,68 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - cdma - queries telephony manager`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun roaming_cdma_queriesTelephonyManager() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
|
||||
val serviceState = ServiceState()
|
||||
serviceState.roaming = false
|
||||
|
||||
// CDMA roaming is off, GSM roaming is off
|
||||
// CDMA roaming is off, GSM roaming is on
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
cb.onServiceStateChanged(ServiceState().also { it.roaming = true })
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is on
|
||||
// CDMA roaming is on, GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_ON)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
cb.onServiceStateChanged(ServiceState().also { it.roaming = false })
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* [TelephonyManager.getCdmaEnhancedRoamingIndicatorDisplayNumber] returns -1 if the service is
|
||||
* not running or if there is an error while retrieving the cdma ERI
|
||||
*/
|
||||
@Test
|
||||
fun `roaming - gsm - queries service state`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun cdmaRoaming_ignoresNegativeOne() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
|
||||
val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val serviceState = ServiceState()
|
||||
serviceState.roaming = false
|
||||
|
||||
val cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
|
||||
// CDMA roaming is unavailable (-1), GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(-1)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roaming_gsm_queriesServiceState() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
|
||||
cb.onServiceStateChanged(ServiceState().also { it.roaming = false })
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is on
|
||||
serviceState.roaming = true
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
cb.onServiceStateChanged(ServiceState().also { it.roaming = true })
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
@@ -541,8 +577,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `activity - updates from callback`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun activity_updatesFromCallback() =
|
||||
testScope.runTest {
|
||||
var latest: DataActivityModel? = null
|
||||
val job = underTest.dataActivityDirection.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -578,8 +614,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network name - default`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun networkName_default() =
|
||||
testScope.runTest {
|
||||
var latest: NetworkNameModel? = null
|
||||
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -589,8 +625,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network name - uses broadcast info - returns derived`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun networkName_usesBroadcastInfo_returnsDerived() =
|
||||
testScope.runTest {
|
||||
var latest: NetworkNameModel? = null
|
||||
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -606,8 +642,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network name - broadcast not for this sub id - keeps old value`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun networkName_broadcastNotForThisSubId_keepsOldValue() =
|
||||
testScope.runTest {
|
||||
var latest: NetworkNameModel? = null
|
||||
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -631,8 +667,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network name - broadcast has no data - updates to default`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun networkName_broadcastHasNoData_updatesToDefault() =
|
||||
testScope.runTest {
|
||||
var latest: NetworkNameModel? = null
|
||||
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -658,8 +694,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `operatorAlphaShort - tracked`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun operatorAlphaShort_tracked() =
|
||||
testScope.runTest {
|
||||
var latest: String? = null
|
||||
|
||||
val job = underTest.operatorAlphaShort.onEach { latest = it }.launchIn(this)
|
||||
@@ -680,33 +716,45 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `connection model - isInService - not iwlan`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun isInService_notIwlan() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isInService.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val serviceState = ServiceState()
|
||||
serviceState.voiceRegState = STATE_IN_SERVICE
|
||||
serviceState.dataRegState = STATE_IN_SERVICE
|
||||
|
||||
getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
|
||||
getTelephonyCallbackForType<ServiceStateListener>()
|
||||
.onServiceStateChanged(
|
||||
ServiceState().also {
|
||||
it.voiceRegState = STATE_IN_SERVICE
|
||||
it.dataRegState = STATE_IN_SERVICE
|
||||
}
|
||||
)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
serviceState.voiceRegState = STATE_OUT_OF_SERVICE
|
||||
getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
|
||||
getTelephonyCallbackForType<ServiceStateListener>()
|
||||
.onServiceStateChanged(
|
||||
ServiceState().also {
|
||||
it.dataRegState = STATE_IN_SERVICE
|
||||
it.voiceRegState = STATE_OUT_OF_SERVICE
|
||||
}
|
||||
)
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
serviceState.dataRegState = STATE_OUT_OF_SERVICE
|
||||
getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
|
||||
getTelephonyCallbackForType<ServiceStateListener>()
|
||||
.onServiceStateChanged(
|
||||
ServiceState().also {
|
||||
it.voiceRegState = STATE_OUT_OF_SERVICE
|
||||
it.dataRegState = STATE_OUT_OF_SERVICE
|
||||
}
|
||||
)
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `connection model - isInService - is iwlan - voice out of service - data in service`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun isInService_isIwlan_voiceOutOfService_dataInService() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isInService.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -730,8 +778,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `number of levels - uses carrier config`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
fun numberOfLevels_usesCarrierConfig() =
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -756,19 +804,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
|
||||
}
|
||||
|
||||
/** Convenience constructor for SignalStrength */
|
||||
private fun signalStrength(gsmLevel: Int, cdmaLevel: Int, isGsm: Boolean): SignalStrength {
|
||||
val signalStrength = mock<SignalStrength>()
|
||||
whenever(signalStrength.isGsm).thenReturn(isGsm)
|
||||
whenever(signalStrength.level).thenReturn(gsmLevel)
|
||||
val cdmaStrength =
|
||||
mock<CellSignalStrengthCdma>().also { whenever(it.level).thenReturn(cdmaLevel) }
|
||||
whenever(signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java))
|
||||
.thenReturn(listOf(cdmaStrength))
|
||||
|
||||
return signalStrength
|
||||
}
|
||||
|
||||
private fun spnIntent(
|
||||
subId: Int = SUB_1_ID,
|
||||
showSpn: Boolean = true,
|
||||
@@ -785,7 +820,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
private const val SUB_1_ID = 1
|
||||
|
||||
private val DEFAULT_NAME = NetworkNameModel.Default("default name")
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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 android.telephony.ServiceState
|
||||
import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyCallback.CarrierNetworkListener
|
||||
import android.telephony.TelephonyCallback.DataActivityListener
|
||||
import android.telephony.TelephonyCallback.DataConnectionStateListener
|
||||
import android.telephony.TelephonyCallback.DataEnabledListener
|
||||
import android.telephony.TelephonyCallback.DisplayInfoListener
|
||||
import android.telephony.TelephonyCallback.ServiceStateListener
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
||||
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.MobileInputLogger
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
|
||||
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.model.SystemUiCarrierConfig
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
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.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/**
|
||||
* Test class to stress test the TelephonyCallbacks that we listen to. In particular, the callbacks
|
||||
* all come back in on a single listener (for reasons defined in the system). This test is built to
|
||||
* ensure that we don't miss any important callbacks.
|
||||
*
|
||||
* Kind of like an interaction test case build just for [TelephonyCallback]
|
||||
*
|
||||
* The list of telephony callbacks we use is: [TelephonyCallback.CarrierNetworkListener]
|
||||
* [TelephonyCallback.DataActivityListener] [TelephonyCallback.DataConnectionStateListener]
|
||||
* [TelephonyCallback.DataEnabledListener] [TelephonyCallback.DisplayInfoListener]
|
||||
* [TelephonyCallback.ServiceStateListener] [TelephonyCallback.SignalStrengthsListener]
|
||||
*
|
||||
* Because each of these callbacks comes in on the same callbackFlow, collecting on a field backed
|
||||
* by only a single callback can immediately create backpressure on the other fields related to a
|
||||
* mobile connection.
|
||||
*
|
||||
* This test should be designed to test _at least_ each individual callback in a smoke-test fashion.
|
||||
* The way we will achieve this is as follows:
|
||||
* 1. Start up a listener (A) collecting on a field which is _not under test_
|
||||
* 2. Send a single event to a telephony callback which supports the field under test (B)
|
||||
* 3. Send many (may be as few as 2) events to the callback backing A to ensure we start seeing
|
||||
* backpressure on other fields NOTE: poor handling of backpressure here would normally cause B
|
||||
* to get dropped
|
||||
* 4. Start up a new collector for B
|
||||
* 5. Assert that B has the state sent in step #2
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileConnectionRepositoryImpl
|
||||
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
|
||||
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@Mock private lateinit var logger: MobileInputLogger
|
||||
@Mock private lateinit var tableLogger: TableLogBuffer
|
||||
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val systemUiCarrierConfig =
|
||||
SystemUiCarrierConfig(
|
||||
SUB_1_ID,
|
||||
SystemUiCarrierConfigTest.createTestConfig(),
|
||||
)
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
|
||||
|
||||
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
|
||||
|
||||
underTest =
|
||||
MobileConnectionRepositoryImpl(
|
||||
context,
|
||||
SUB_1_ID,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
telephonyManager,
|
||||
systemUiCarrierConfig,
|
||||
fakeBroadcastDispatcher,
|
||||
mobileMappings,
|
||||
testDispatcher,
|
||||
logger,
|
||||
tableLogger,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun carrierNetworkChangeListener_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
val callback = getTelephonyCallbackForType<CarrierNetworkListener>()
|
||||
callback.onCarrierNetworkChange(true)
|
||||
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
activityJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dataActivityLate_noisyDisplayInfo() =
|
||||
testScope.runTest {
|
||||
var latest: DataActivityModel? = null
|
||||
|
||||
// start collecting displayInfo; don't care about the result
|
||||
val displayInfoJob = underTest.resolvedNetworkType.launchIn(this)
|
||||
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
activityCallback.onDataActivity(DATA_ACTIVITY_INOUT)
|
||||
|
||||
val displayInfoCallback = getTelephonyCallbackForType<DisplayInfoListener>()
|
||||
val type1 = NETWORK_TYPE_UNKNOWN
|
||||
val type2 = NETWORK_TYPE_LTE
|
||||
val t1 =
|
||||
mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type1) }
|
||||
val t2 =
|
||||
mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type2) }
|
||||
|
||||
flipDisplayInfo(100, listOf(t1, t2), displayInfoCallback)
|
||||
|
||||
val job = underTest.dataActivityDirection.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest)
|
||||
.isEqualTo(
|
||||
DataActivityModel(
|
||||
hasActivityIn = true,
|
||||
hasActivityOut = true,
|
||||
)
|
||||
)
|
||||
|
||||
displayInfoJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dataConnectionStateListener_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: DataConnectionState? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
|
||||
val connectionCallback = getTelephonyCallbackForType<DataConnectionStateListener>()
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
connectionCallback.onDataConnectionStateChanged(
|
||||
TelephonyManager.DATA_CONNECTED,
|
||||
200 /* unused */
|
||||
)
|
||||
|
||||
// Send a bunch of events that we don't care about, to overrun the replay buffer
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val connectionJob = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEqualTo(DataConnectionState.Connected)
|
||||
|
||||
activityJob.cancel()
|
||||
connectionJob.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dataEnabledLate_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
|
||||
val enabledCallback = getTelephonyCallbackForType<DataEnabledListener>()
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
enabledCallback.onDataEnabledChanged(true, 1 /* unused */)
|
||||
|
||||
// Send a bunch of events that we don't care about, to overrun the replay buffer
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
activityJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayInfoLate_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: ResolvedNetworkType? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
|
||||
val displayInfoCallback = getTelephonyCallbackForType<DisplayInfoListener>()
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
val type = NETWORK_TYPE_LTE
|
||||
val expected = ResolvedNetworkType.DefaultNetworkType(mobileMappings.toIconKey(type))
|
||||
val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
|
||||
displayInfoCallback.onDisplayInfoChanged(ti)
|
||||
|
||||
// Send a bunch of events that we don't care about, to overrun the replay buffer
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEqualTo(expected)
|
||||
|
||||
activityJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun serviceStateListener_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
|
||||
val serviceStateCallback = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
// isEmergencyOnly comes in
|
||||
val serviceState = ServiceState()
|
||||
serviceState.isEmergencyOnly = true
|
||||
serviceStateCallback.onServiceStateChanged(serviceState)
|
||||
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val job = underTest.isEmergencyOnly.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
activityJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun signalStrengthsListenerLate_noisyActivity() =
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
|
||||
// Start collecting data activity; don't care about the result
|
||||
val activityJob = underTest.dataActivityDirection.launchIn(this)
|
||||
val activityCallback = getTelephonyCallbackForType<DataActivityListener>()
|
||||
|
||||
val callback = getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>()
|
||||
val strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = true)
|
||||
callback.onSignalStrengthsChanged(strength)
|
||||
|
||||
flipActivity(100, activityCallback)
|
||||
|
||||
val job = underTest.cdmaLevel.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isEqualTo(2)
|
||||
|
||||
activityJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun flipActivity(
|
||||
times: Int,
|
||||
callback: DataActivityListener,
|
||||
) {
|
||||
repeat(times) { index -> callback.onDataActivity(index % 4) }
|
||||
}
|
||||
|
||||
private fun flipDisplayInfo(
|
||||
times: Int,
|
||||
infos: List<TelephonyDisplayInfo>,
|
||||
callback: DisplayInfoListener,
|
||||
) {
|
||||
val len = infos.size
|
||||
repeat(times) { index -> callback.onDisplayInfoChanged(infos[index % len]) }
|
||||
}
|
||||
|
||||
private inline fun <reified T> getTelephonyCallbackForType(): T {
|
||||
return getTelephonyCallbackForType(telephonyManager)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SUB_1_ID = 1
|
||||
|
||||
private val DEFAULT_NAME = NetworkNameModel.Default("default name")
|
||||
private const val SEP = "-"
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
|
||||
import android.telephony.CellSignalStrengthCdma
|
||||
import android.telephony.SignalStrength
|
||||
import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyManager
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
@@ -31,6 +35,19 @@ object MobileTelephonyHelpers {
|
||||
return callbackCaptor.allValues
|
||||
}
|
||||
|
||||
/** Convenience constructor for SignalStrength */
|
||||
fun signalStrength(gsmLevel: Int, cdmaLevel: Int, isGsm: Boolean): SignalStrength {
|
||||
val signalStrength = mock<SignalStrength>()
|
||||
whenever(signalStrength.isGsm).thenReturn(isGsm)
|
||||
whenever(signalStrength.level).thenReturn(gsmLevel)
|
||||
val cdmaStrength =
|
||||
mock<CellSignalStrengthCdma>().also { whenever(it.level).thenReturn(cdmaLevel) }
|
||||
whenever(signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java))
|
||||
.thenReturn(listOf(cdmaStrength))
|
||||
|
||||
return signalStrength
|
||||
}
|
||||
|
||||
inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
|
||||
val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
|
||||
assertThat(cbs.size).isEqualTo(1)
|
||||
|
||||
Reference in New Issue
Block a user