Merge changes I39698d4d,I8c8b67a9 into tm-qpr-dev

* changes:
  [Sb refactor] Move active data repo to connections repo
  [Sb refactor] Address code comments
This commit is contained in:
Evan Laird
2023-02-09 21:44:28 +00:00
committed by Android (Google) Code Review
8 changed files with 262 additions and 78 deletions

View File

@@ -163,8 +163,7 @@ public class StatusBarIconControllerImpl implements Tunable,
for (int i = currentSlots.size() - 1; i >= 0; i--) { for (int i = currentSlots.size() - 1; i >= 0; i--) {
Slot s = currentSlots.get(i); Slot s = currentSlots.get(i);
slotsToReAdd.put(s, s.getHolderList()); slotsToReAdd.put(s, s.getHolderList());
// Don't force here because the new pipeline properly handles the tuner settings removeAllIconsForSlot(s.getName(), /* fromNewPipeline */ false);
removeAllIconsForSlot(s.getName(), /* force */ false);
} }
// Add them all back // Add them all back
@@ -286,7 +285,7 @@ public class StatusBarIconControllerImpl implements Tunable,
// Because of the way we cache the icon holders, we need to remove everything any time // Because of the way we cache the icon holders, we need to remove everything any time
// we get a new set of subscriptions. This might change in the future, but is required // we get a new set of subscriptions. This might change in the future, but is required
// to support demo mode for now // to support demo mode for now
removeAllIconsForSlot(slotName, /* force */ true); removeAllIconsForSlot(slotName, /* fromNewPipeline */ true);
Collections.reverse(subIds); Collections.reverse(subIds);
@@ -453,13 +452,13 @@ public class StatusBarIconControllerImpl implements Tunable,
/** */ /** */
@Override @Override
public void removeAllIconsForSlot(String slotName) { public void removeAllIconsForSlot(String slotName) {
removeAllIconsForSlot(slotName, /* force */ false); removeAllIconsForSlot(slotName, /* fromNewPipeline */ false);
} }
private void removeAllIconsForSlot(String slotName, Boolean force) { private void removeAllIconsForSlot(String slotName, boolean fromNewPipeline) {
// If the new pipeline is on for this icon, don't allow removal, since the new pipeline // If the new pipeline is on for this icon, don't allow removal, since the new pipeline
// will never call this method // will never call this method
if (!force && mStatusBarPipelineFlags.isIconControlledByFlags(slotName)) { if (!fromNewPipeline && mStatusBarPipelineFlags.isIconControlledByFlags(slotName)) {
Log.i(TAG, "Ignoring removal of (" + slotName + "). " Log.i(TAG, "Ignoring removal of (" + slotName + "). "
+ "It should be controlled elsewhere"); + "It should be controlled elsewhere");
return; return;

View File

@@ -34,8 +34,14 @@ interface MobileConnectionsRepository {
/** Observable list of current mobile subscriptions */ /** Observable list of current mobile subscriptions */
val subscriptions: StateFlow<List<SubscriptionModel>> val subscriptions: StateFlow<List<SubscriptionModel>>
/** Observable for the subscriptionId of the current mobile data connection */ /**
val activeMobileDataSubscriptionId: StateFlow<Int> * Observable for the subscriptionId of the current mobile data connection. Null if we don't
* have a valid subscription id
*/
val activeMobileDataSubscriptionId: StateFlow<Int?>
/** Repo that tracks the current [activeMobileDataSubscriptionId] */
val activeMobileDataRepository: StateFlow<MobileConnectionRepository?>
/** /**
* Observable event for when the active data sim switches but the group stays the same. E.g., * Observable event for when the active data sim switches but the group stays the same. E.g.,

View File

@@ -47,7 +47,6 @@ import kotlinx.coroutines.flow.stateIn
* interface in its own repository, completely separate from the real version, while still using all * interface in its own repository, completely separate from the real version, while still using all
* of the prod implementations for the rest of the pipeline (interactors and onward). Looks * of the prod implementations for the rest of the pipeline (interactors and onward). Looks
* something like this: * something like this:
*
* ``` * ```
* RealRepository * RealRepository
* │ * │
@@ -115,7 +114,7 @@ constructor(
.flatMapLatest { it.subscriptions } .flatMapLatest { it.subscriptions }
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.subscriptions.value) .stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.subscriptions.value)
override val activeMobileDataSubscriptionId: StateFlow<Int> = override val activeMobileDataSubscriptionId: StateFlow<Int?> =
activeRepo activeRepo
.flatMapLatest { it.activeMobileDataSubscriptionId } .flatMapLatest { it.activeMobileDataSubscriptionId }
.stateIn( .stateIn(
@@ -124,6 +123,15 @@ constructor(
realRepository.activeMobileDataSubscriptionId.value realRepository.activeMobileDataSubscriptionId.value
) )
override val activeMobileDataRepository: StateFlow<MobileConnectionRepository?> =
activeRepo
.flatMapLatest { it.activeMobileDataRepository }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
realRepository.activeMobileDataRepository.value
)
override val activeSubChangedInGroupEvent: Flow<Unit> = override val activeSubChangedInGroupEvent: Flow<Unit> =
activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent } activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }

View File

@@ -55,6 +55,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@@ -121,6 +122,15 @@ constructor(
subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
) )
override val activeMobileDataRepository: StateFlow<MobileConnectionRepository?> =
activeMobileDataSubscriptionId
.map { getRepoForSubId(it) }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
getRepoForSubId(activeMobileDataSubscriptionId.value)
)
// TODO(b/261029387): consider adding a demo command for this // TODO(b/261029387): consider adding a demo command for this
override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf() override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf()

View File

@@ -159,13 +159,16 @@ constructor(
) )
.stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf()) .stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
/** StateFlow that keeps track of the current active mobile data subscription */ override val activeMobileDataSubscriptionId: StateFlow<Int?> =
override val activeMobileDataSubscriptionId: StateFlow<Int> =
conflatedCallbackFlow { conflatedCallbackFlow {
val callback = val callback =
object : TelephonyCallback(), ActiveDataSubscriptionIdListener { object : TelephonyCallback(), ActiveDataSubscriptionIdListener {
override fun onActiveDataSubscriptionIdChanged(subId: Int) { override fun onActiveDataSubscriptionIdChanged(subId: Int) {
trySend(subId) if (subId != INVALID_SUBSCRIPTION_ID) {
trySend(subId)
} else {
trySend(null)
}
} }
} }
@@ -179,7 +182,18 @@ constructor(
columnName = "activeSubId", columnName = "activeSubId",
initialValue = INVALID_SUBSCRIPTION_ID, initialValue = INVALID_SUBSCRIPTION_ID,
) )
.stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID) .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
override val activeMobileDataRepository =
activeMobileDataSubscriptionId
.map { activeSubId ->
if (activeSubId == null) {
null
} else {
getOrCreateRepoForSubId(activeSubId)
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> = private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
MutableSharedFlow(extraBufferCapacity = 1) MutableSharedFlow(extraBufferCapacity = 1)
@@ -240,10 +254,13 @@ constructor(
) )
} }
return subIdRepositoryCache[subId] return getOrCreateRepoForSubId(subId)
?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
} }
private fun getOrCreateRepoForSubId(subId: Int) =
subIdRepositoryCache[subId]
?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> = override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
conflatedCallbackFlow { conflatedCallbackFlow {
@@ -292,7 +309,9 @@ constructor(
override val activeSubChangedInGroupEvent = override val activeSubChangedInGroupEvent =
activeMobileDataSubscriptionId activeMobileDataSubscriptionId
.pairwise() .pairwise()
.mapNotNull { (prevVal: Int, newVal: Int) -> .mapNotNull { (prevVal: Int?, newVal: Int?) ->
if (prevVal == null || newVal == null) return@mapNotNull null
val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid
val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid
@@ -300,15 +319,7 @@ constructor(
} }
.flowOn(bgDispatcher) .flowOn(bgDispatcher)
private fun isValidSubId(subId: Int): Boolean { private fun isValidSubId(subId: Int): Boolean = checkSub(subId, subscriptions.value)
subscriptions.value.forEach {
if (it.subscriptionId == subId) {
return true
}
}
return false
}
@VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache @VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
@@ -335,12 +346,27 @@ constructor(
private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) { private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) {
// Remove any connection repository from the cache that isn't in the new set of IDs. They // 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 // will get garbage collected once their subscribers go away
val currentValidSubscriptionIds = newInfos.map { it.subscriptionId }
subIdRepositoryCache = subIdRepositoryCache =
subIdRepositoryCache subIdRepositoryCache.filter { checkSub(it.key, newInfos) }.toMutableMap()
.filter { currentValidSubscriptionIds.contains(it.key) } }
.toMutableMap()
/**
* True if the checked subId is in the list of current subs or the active mobile data subId
*
* @param checkedSubs the list to validate [subId] against. To invalidate the cache, pass in the
* new subscription list. Otherwise use [subscriptions.value] to validate a subId against the
* current known subscriptions
*/
private fun checkSub(subId: Int, checkedSubs: List<SubscriptionModel>): Boolean {
if (activeMobileDataSubscriptionId.value == subId) return true
checkedSubs.forEach {
if (it.subscriptionId == subId) {
return true
}
}
return false
} }
private suspend fun fetchSubscriptionsList(): List<SubscriptionInfo> = private suspend fun fetchSubscriptionsList(): List<SubscriptionInfo> =

View File

@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
@@ -118,22 +117,8 @@ constructor(
userSetupRepo: UserSetupRepository, userSetupRepo: UserSetupRepository,
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
) : MobileIconsInteractor { ) : MobileIconsInteractor {
private val activeMobileDataSubscriptionId =
mobileConnectionsRepo.activeMobileDataSubscriptionId
private val activeMobileDataConnectionRepo: StateFlow<MobileConnectionRepository?> =
activeMobileDataSubscriptionId
.mapLatest { activeId ->
if (activeId == INVALID_SUBSCRIPTION_ID) {
null
} else {
mobileConnectionsRepo.getRepoForSubId(activeId)
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> = override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
activeMobileDataConnectionRepo mobileConnectionsRepo.activeMobileDataRepository
.flatMapLatest { it?.dataEnabled ?: flowOf(false) } .flatMapLatest { it?.dataEnabled ?: flowOf(false) }
.stateIn(scope, SharingStarted.WhileSubscribed(), false) .stateIn(scope, SharingStarted.WhileSubscribed(), false)
@@ -154,35 +139,37 @@ constructor(
* and by checking which subscription is opportunistic, or which one is active. * and by checking which subscription is opportunistic, or which one is active.
*/ */
override val filteredSubscriptions: Flow<List<SubscriptionModel>> = override val filteredSubscriptions: Flow<List<SubscriptionModel>> =
combine(unfilteredSubscriptions, activeMobileDataSubscriptionId) { unfilteredSubs, activeId combine(
-> unfilteredSubscriptions,
// Based on the old logic, mobileConnectionsRepo.activeMobileDataSubscriptionId,
if (unfilteredSubs.size != 2) { ) { unfilteredSubs, activeId ->
return@combine unfilteredSubs // Based on the old logic,
} if (unfilteredSubs.size != 2) {
return@combine unfilteredSubs
}
val info1 = unfilteredSubs[0] val info1 = unfilteredSubs[0]
val info2 = unfilteredSubs[1] val info2 = unfilteredSubs[1]
// If both subscriptions are primary, show both // If both subscriptions are primary, show both
if (!info1.isOpportunistic && !info2.isOpportunistic) { if (!info1.isOpportunistic && !info2.isOpportunistic) {
return@combine unfilteredSubs return@combine unfilteredSubs
} }
// NOTE: at this point, we are now returning a single SubscriptionInfo // NOTE: at this point, we are now returning a single SubscriptionInfo
// If carrier required, always show the icon of the primary subscription. // If carrier required, always show the icon of the primary subscription.
// Otherwise, show whichever subscription is currently active for internet. // Otherwise, show whichever subscription is currently active for internet.
if (carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault) { if (carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault) {
// return the non-opportunistic info // return the non-opportunistic info
return@combine if (info1.isOpportunistic) listOf(info2) else listOf(info1) return@combine if (info1.isOpportunistic) listOf(info2) else listOf(info1)
} else {
return@combine if (info1.subscriptionId == activeId) {
listOf(info1)
} else { } else {
listOf(info2) return@combine if (info1.subscriptionId == activeId) {
listOf(info1)
} else {
listOf(info2)
}
} }
} }
}
.distinctUntilChanged() .distinctUntilChanged()
.logDiffsForTable( .logDiffsForTable(
tableLogger, tableLogger,

View File

@@ -55,8 +55,12 @@ class FakeMobileConnectionsRepository(
private val _subscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf()) private val _subscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
override val subscriptions = _subscriptions override val subscriptions = _subscriptions
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID) private val _activeMobileDataSubscriptionId = MutableStateFlow<Int?>(null)
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
private val _activeMobileRepository = MutableStateFlow<MobileConnectionRepository?>(null)
override val activeMobileDataRepository = _activeMobileRepository
override val activeSubChangedInGroupEvent: MutableSharedFlow<Unit> = MutableSharedFlow() override val activeSubChangedInGroupEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID) private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
@@ -66,6 +70,7 @@ class FakeMobileConnectionsRepository(
override val defaultMobileNetworkConnectivity = _mobileConnectivity override val defaultMobileNetworkConnectivity = _mobileConnectivity
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>() private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
override fun getRepoForSubId(subId: Int): MobileConnectionRepository { override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId] return subIdRepos[subId]
?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it } ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
@@ -92,7 +97,14 @@ class FakeMobileConnectionsRepository(
} }
fun setActiveMobileDataSubscriptionId(subId: Int) { fun setActiveMobileDataSubscriptionId(subId: Int) {
_activeMobileDataSubscriptionId.value = subId // Simulate the filtering that the repo does
if (subId == INVALID_SUBSCRIPTION_ID) {
_activeMobileDataSubscriptionId.value = null
_activeMobileRepository.value = null
} else {
_activeMobileDataSubscriptionId.value = subId
_activeMobileRepository.value = getRepoForSubId(subId)
}
} }
fun setMobileConnectionRepositoryMap(connections: Map<Int, MobileConnectionRepository>) { fun setMobileConnectionRepositoryMap(connections: Map<Int, MobileConnectionRepository>) {

View File

@@ -26,6 +26,7 @@ import android.os.ParcelUuid
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
@@ -39,6 +40,7 @@ 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.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName 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.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
@@ -55,6 +57,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@@ -256,10 +259,9 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
} }
@Test @Test
fun testActiveDataSubscriptionId_initialValueIsInvalidId() = fun testActiveDataSubscriptionId_initialValueIsNull() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
assertThat(underTest.activeMobileDataSubscriptionId.value) assertThat(underTest.activeMobileDataSubscriptionId.value).isEqualTo(null)
.isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
} }
@Test @Test
@@ -277,6 +279,140 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun activeSubId_nullIfInvalidSubIdIsReceived() =
runBlocking(IMMEDIATE) {
var latest: Int? = null
val job = underTest.activeMobileDataSubscriptionId.onEach { latest = it }.launchIn(this)
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
assertThat(latest).isNotNull()
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
assertThat(latest).isNull()
job.cancel()
}
@Test
fun activeRepo_initiallyNull() {
assertThat(underTest.activeMobileDataRepository.value).isNull()
}
@Test
fun activeRepo_updatesWithActiveDataId() =
runBlocking(IMMEDIATE) {
var latest: MobileConnectionRepository? = null
val job = underTest.activeMobileDataRepository.onEach { latest = it }.launchIn(this)
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
assertThat(latest?.subId).isEqualTo(SUB_2_ID)
job.cancel()
}
@Test
fun activeRepo_nullIfActiveDataSubIdBecomesInvalid() =
runBlocking(IMMEDIATE) {
var latest: MobileConnectionRepository? = null
val job = underTest.activeMobileDataRepository.onEach { latest = it }.launchIn(this)
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
assertThat(latest).isNotNull()
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
assertThat(latest).isNull()
job.cancel()
}
@Test
/** Regression test for b/268146648. */
fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() =
runBlocking(IMMEDIATE) {
var activeRepo: MobileConnectionRepository? = null
var subscriptions: List<SubscriptionModel>? = null
val activeRepoJob =
underTest.activeMobileDataRepository.onEach { activeRepo = it }.launchIn(this)
val subscriptionsJob =
underTest.subscriptions.onEach { subscriptions = it }.launchIn(this)
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
assertThat(subscriptions).isEmpty()
assertThat(activeRepo).isNotNull()
activeRepoJob.cancel()
subscriptionsJob.cancel()
}
@Test
fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
runBlocking(IMMEDIATE) {
var latest: MobileConnectionRepository? = null
var subscriptions: List<SubscriptionModel>? = null
val activeSubIdJob =
underTest.activeMobileDataSubscriptionId
.filterNotNull()
.onEach { latest = underTest.getRepoForSubId(it) }
.launchIn(this)
val subscriptionsJob =
underTest.subscriptions.onEach { subscriptions = it }.launchIn(this)
// Active data subscription id is sent, but no subscription change has been posted yet
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
// Subscriptions list is empty
assertThat(subscriptions).isEmpty()
// getRepoForSubId does not throw
assertThat(latest).isNotNull()
activeSubIdJob.cancel()
subscriptionsJob.cancel()
}
@Test
fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
runBlocking(IMMEDIATE) {
var activeRepo: MobileConnectionRepository? = null
val job = underTest.activeMobileDataRepository.onEach { activeRepo = it }.launchIn(this)
val subscriptionsJob = underTest.subscriptions.launchIn(this)
// GIVEN active repo is updated before the subscription list updates
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
assertThat(activeRepo).isNotNull()
// GIVEN the subscription list is then updated which includes the active data sub id
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
// WHEN requesting a connection repository for the subscription
val newRepo = underTest.getRepoForSubId(SUB_2_ID)
// THEN the newly request repo has been cached and reused
assertThat(activeRepo).isSameInstanceAs(newRepo)
job.cancel()
subscriptionsJob.cancel()
}
@Test @Test
fun testConnectionRepository_validSubId_isCached() = fun testConnectionRepository_validSubId_isCached() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
@@ -477,7 +613,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
} }
@Test @Test
fun `connection repository - log buffer contains sub id in its name`() = fun connectionRepository_logBufferContainsSubIdInItsName() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
val job = underTest.subscriptions.launchIn(this) val job = underTest.subscriptions.launchIn(this)
@@ -693,7 +829,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
} }
@Test @Test
fun `active data change - in same group - emits unit`() = fun activeDataChange_inSameGroup_emitsUnit() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
var latest: Unit? = null var latest: Unit? = null
val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this) val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
@@ -709,7 +845,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
} }
@Test @Test
fun `active data change - not in same group - does not emit`() = fun activeDataChange_notInSameGroup_doesNotEmit() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
var latest: Unit? = null var latest: Unit? = null
val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this) val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)