Merge changes I4977aa47,I87686213,If4bd3e31,I727ad3ca,Ib02a1eb6 into tm-qpr-dev am: 18917a1820

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20665129

Change-Id: I6fbc97e490f2f6b521b51861a99cfa96aa1238b5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Evan Laird
2022-12-08 23:21:33 +00:00
committed by Automerger Merge Worker
27 changed files with 580 additions and 352 deletions

View File

@@ -40,6 +40,8 @@ import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import java.util.ArrayList;
import java.util.List;
@@ -50,20 +52,25 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
private final LinearLayout mStatusIcons;
private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>();
private final int mIconSize;
private StatusBarWifiView mWifiView;
private boolean mDemoMode;
private int mColor;
private final MobileIconsViewModel mMobileIconsViewModel;
public DemoStatusIcons(
LinearLayout statusIcons,
MobileIconsViewModel mobileIconsViewModel,
int iconSize
) {
super(statusIcons.getContext());
mStatusIcons = statusIcons;
mIconSize = iconSize;
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
mMobileIconsViewModel = mobileIconsViewModel;
if (statusIcons instanceof StatusIconContainer) {
setShouldRestrictIcons(((StatusIconContainer) statusIcons).isRestrictingIcons());
@@ -71,7 +78,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
setShouldRestrictIcons(false);
}
setLayoutParams(mStatusIcons.getLayoutParams());
setPadding(mStatusIcons.getPaddingLeft(),mStatusIcons.getPaddingTop(),
setPadding(mStatusIcons.getPaddingLeft(), mStatusIcons.getPaddingTop(),
mStatusIcons.getPaddingRight(), mStatusIcons.getPaddingBottom());
setOrientation(mStatusIcons.getOrientation());
setGravity(Gravity.CENTER_VERTICAL); // no LL.getGravity()
@@ -115,6 +122,8 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
public void onDemoModeFinished() {
mDemoMode = false;
mStatusIcons.setVisibility(View.VISIBLE);
mModernMobileViews.clear();
mMobileViews.clear();
setVisibility(View.GONE);
}
@@ -268,6 +277,24 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
addView(view, getChildCount(), createLayoutParams());
}
/**
* Add a {@link ModernStatusBarMobileView}
* @param mobileContext possibly mcc/mnc overridden mobile context
* @param subId the subscriptionId for this mobile view
*/
public void addModernMobileView(Context mobileContext, int subId) {
Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind(
mobileContext,
"mobile",
mMobileIconsViewModel.viewModelForSub(subId)
);
// mobile always goes at the end
mModernMobileViews.add(view);
addView(view, getChildCount(), createLayoutParams());
}
/**
* Apply an update to a mobile icon view for the given {@link MobileIconState}. For
* compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
@@ -292,12 +319,19 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
if (view.getSlot().equals("wifi")) {
removeView(mWifiView);
mWifiView = null;
} else {
} else if (view instanceof StatusBarMobileView) {
StatusBarMobileView mobileView = matchingMobileView(view);
if (mobileView != null) {
removeView(mobileView);
mMobileViews.remove(mobileView);
}
} else if (view instanceof ModernStatusBarMobileView) {
ModernStatusBarMobileView mobileView = matchingModernMobileView(
(ModernStatusBarMobileView) view);
if (mobileView != null) {
removeView(mobileView);
mModernMobileViews.remove(mobileView);
}
}
}
@@ -316,6 +350,16 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
return null;
}
private ModernStatusBarMobileView matchingModernMobileView(ModernStatusBarMobileView other) {
for (ModernStatusBarMobileView v : mModernMobileViews) {
if (v.getSubId() == other.getSubId()) {
return v;
}
}
return null;
}
private LayoutParams createLayoutParams() {
return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
}

View File

@@ -536,8 +536,7 @@ public interface StatusBarIconController {
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
// TODO (b/249790009): demo mode should be handled at the data layer in the
// new pipeline
mDemoStatusIcons.addModernMobileView(mContext, subId);
}
return view;
@@ -565,11 +564,13 @@ public interface StatusBarIconController {
private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
String slot, int subId) {
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
return ModernStatusBarMobileView
.constructAndBind(
mContext,
mobileContext,
slot,
mMobileIconsViewModel.viewModelForSub(subId));
mMobileIconsViewModel.viewModelForSub(subId)
);
}
protected LinearLayout.LayoutParams onCreateLayoutParams() {
@@ -704,7 +705,7 @@ public interface StatusBarIconController {
}
protected DemoStatusIcons createDemoStatusIcons() {
return new DemoStatusIcons((LinearLayout) mGroup, mIconSize);
return new DemoStatusIcons((LinearLayout) mGroup, mMobileIconsViewModel, mIconSize);
}
}
}

View File

@@ -276,6 +276,11 @@ public class StatusBarIconControllerImpl implements Tunable,
String slotName = mContext.getString(com.android.internal.R.string.status_bar_mobile);
Slot mobileSlot = mStatusBarIconList.getSlot(slotName);
// 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
// to support demo mode for now
removeAllIconsForSlot(slotName);
Collections.reverse(subIds);
for (Integer subId : subIds) {

View File

@@ -27,7 +27,6 @@ import android.telephony.TelephonyCallback.ServiceStateListener
import android.telephony.TelephonyCallback.SignalStrengthsListener
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
/**
@@ -39,7 +38,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionS
* any new field that needs to be tracked should be copied into this data class rather than
* threading complex system objects through the pipeline.
*/
data class MobileSubscriptionModel(
data class MobileConnectionModel(
/** From [ServiceStateListener.onServiceStateChanged] */
val isEmergencyOnly: Boolean = false,
@@ -65,5 +64,5 @@ data class MobileSubscriptionModel(
* [resolvedNetworkType] is the [TelephonyDisplayInfo.getOverrideNetworkType] if it exists or
* [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon
*/
val resolvedNetworkType: ResolvedNetworkType = DefaultNetworkType(NETWORK_TYPE_UNKNOWN),
val resolvedNetworkType: ResolvedNetworkType = ResolvedNetworkType.UnknownNetworkType,
)

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.telephony.Annotation.NetworkType
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
/**
@@ -26,8 +27,20 @@ import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
*/
sealed interface ResolvedNetworkType {
@NetworkType val type: Int
val lookupKey: String
object UnknownNetworkType : ResolvedNetworkType {
override val type: Int = NETWORK_TYPE_UNKNOWN
override val lookupKey: String = "unknown"
}
data class DefaultNetworkType(
@NetworkType override val type: Int,
override val lookupKey: String,
) : ResolvedNetworkType
data class OverrideNetworkType(
@NetworkType override val type: Int,
override val lookupKey: String,
) : ResolvedNetworkType
}
data class DefaultNetworkType(@NetworkType override val type: Int) : ResolvedNetworkType
data class OverrideNetworkType(@NetworkType override val type: Int) : ResolvedNetworkType

View File

@@ -0,0 +1,32 @@
/*
* 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.model
/**
* SystemUI representation of [SubscriptionInfo]. Currently we only use two fields on the
* subscriptions themselves: subscriptionId and isOpportunistic. Any new fields that we need can be
* added below and provided in the repository classes
*/
data class SubscriptionModel(
val subscriptionId: Int,
/**
* True if the subscription that this model represents has [SubscriptionInfo.isOpportunistic].
* Opportunistic networks are networks with limited coverage, and we use this bit to determine
* filtering in certain cases. See [MobileIconsInteractor] for the filtering logic
*/
val isOpportunistic: Boolean = false,
)

View File

@@ -20,7 +20,7 @@ import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -36,11 +36,13 @@ import kotlinx.coroutines.flow.StateFlow
* eventually becomes a single icon in the status bar.
*/
interface MobileConnectionRepository {
/** The subscriptionId that this connection represents */
val subId: Int
/**
* A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
* listener + model.
*/
val subscriptionModelFlow: Flow<MobileSubscriptionModel>
val connectionInfo: Flow<MobileConnectionModel>
/** Observable tracking [TelephonyManager.isDataConnectionAllowed] */
val dataEnabled: StateFlow<Boolean>
/**

View File

@@ -17,11 +17,10 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.provider.Settings
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -31,14 +30,11 @@ import kotlinx.coroutines.flow.StateFlow
*/
interface MobileConnectionsRepository {
/** Observable list of current mobile subscriptions */
val subscriptionsFlow: Flow<List<SubscriptionInfo>>
val subscriptions: StateFlow<List<SubscriptionModel>>
/** Observable for the subscriptionId of the current mobile data connection */
val activeMobileDataSubscriptionId: StateFlow<Int>
/** Observable for [MobileMappings.Config] tracking the defaults */
val defaultDataSubRatConfig: StateFlow<Config>
/** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
val defaultDataSubId: StateFlow<Int>
@@ -50,4 +46,10 @@ interface MobileConnectionsRepository {
/** Observe changes to the [Settings.Global.MOBILE_DATA] setting */
val globalMobileDataSettingChangedEvent: Flow<Unit>
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: Flow<Map<String, MobileIconGroup>>
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
val defaultMobileIconGroup: Flow<MobileIconGroup>
}

View File

@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.os.Bundle
import android.telephony.SubscriptionInfo
import androidx.annotation.VisibleForTesting
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.SignalIcon
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
import javax.inject.Inject
@@ -109,14 +109,10 @@ constructor(
}
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository)
override val subscriptionsFlow: StateFlow<List<SubscriptionInfo>> =
override val subscriptions: StateFlow<List<SubscriptionModel>> =
activeRepo
.flatMapLatest { it.subscriptionsFlow }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
realRepository.subscriptionsFlow.value
)
.flatMapLatest { it.subscriptions }
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.subscriptions.value)
override val activeMobileDataSubscriptionId: StateFlow<Int> =
activeRepo
@@ -127,14 +123,11 @@ constructor(
realRepository.activeMobileDataSubscriptionId.value
)
override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
activeRepo
.flatMapLatest { it.defaultDataSubRatConfig }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
realRepository.defaultDataSubRatConfig.value
)
override val defaultMobileIconMapping: Flow<Map<String, SignalIcon.MobileIconGroup>> =
activeRepo.flatMapLatest { it.defaultMobileIconMapping }
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
override val defaultDataSubId: StateFlow<Int> =
activeRepo

View File

@@ -17,26 +17,18 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.content.Context
import android.telephony.Annotation
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
import android.telephony.TelephonyManager.NETWORK_TYPE_GSM
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_NR
import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import android.util.Log
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
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.data.repository.demo.model.FakeNetworkEventModel
@@ -49,7 +41,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -67,61 +61,40 @@ constructor(
private var demoCommandJob: Job? = null
private val connectionRepoCache = mutableMapOf<Int, DemoMobileConnectionRepository>()
private val subscriptionInfoCache = mutableMapOf<Int, SubscriptionInfo>()
private var connectionRepoCache = mutableMapOf<Int, DemoMobileConnectionRepository>()
private val subscriptionInfoCache = mutableMapOf<Int, SubscriptionModel>()
val demoModeFinishedEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
private val _subscriptions = MutableStateFlow<List<SubscriptionInfo>>(listOf())
override val subscriptionsFlow =
private val _subscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
override val subscriptions =
_subscriptions
.onEach { infos -> dropUnusedReposFromCache(infos) }
.stateIn(scope, SharingStarted.WhileSubscribed(), _subscriptions.value)
private fun dropUnusedReposFromCache(newInfos: List<SubscriptionInfo>) {
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
val currentValidSubscriptionIds = newInfos.map { it.subscriptionId }
connectionRepoCache.keys.forEach {
if (!currentValidSubscriptionIds.contains(it)) {
connectionRepoCache.remove(it)
}
}
connectionRepoCache =
connectionRepoCache
.filter { currentValidSubscriptionIds.contains(it.key) }
.toMutableMap()
}
private fun maybeCreateSubscription(subId: Int) {
if (!subscriptionInfoCache.containsKey(subId)) {
createSubscriptionForSubId(subId, subId).also { subscriptionInfoCache[subId] = it }
SubscriptionModel(subscriptionId = subId, isOpportunistic = false).also {
subscriptionInfoCache[subId] = it
}
_subscriptions.value = subscriptionInfoCache.values.toList()
}
}
/** Mimics the old NetworkControllerImpl for now */
private fun createSubscriptionForSubId(subId: Int, slotIndex: Int): SubscriptionInfo {
return SubscriptionInfo(
subId,
"",
slotIndex,
"",
"",
0,
0,
"",
0,
null,
null,
null,
"",
false,
null,
null,
)
}
// TODO(b/261029387): add a command for this value
override val activeMobileDataSubscriptionId =
subscriptionsFlow
subscriptions
.mapLatest { infos ->
// For now, active is just the first in the list
infos.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
@@ -129,12 +102,35 @@ constructor(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
subscriptionsFlow.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
)
/** Demo mode doesn't currently support modifications to the mobile mappings */
override val defaultDataSubRatConfig =
MutableStateFlow(MobileMappings.Config.readConfig(context))
val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config.readConfig(context))
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
/**
* In order to maintain compatibility with the old demo mode shell command API, reverse the
* [MobileMappings] lookup from (NetworkType: String -> Icon: MobileIconGroup), so that we can
* parse the string from the command line into a preferred icon group, and send _a_ valid
* network type for that icon through the pipeline.
*
* Note: collisions don't matter here, because the data source (the command line) only cares
* about the resulting icon, not the underlying network type.
*/
private val mobileMappingsReverseLookup: StateFlow<Map<SignalIcon.MobileIconGroup, String>> =
defaultMobileIconMapping
.mapLatest { networkToIconMap -> networkToIconMap.reverse() }
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
defaultMobileIconMapping.value.reverse()
)
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
// TODO(b/261029387): add a command for this value
override val defaultDataSubId =
@@ -189,7 +185,7 @@ constructor(
connection.dataEnabled.value = true
connection.isDefaultDataSubscription.value = state.dataType != null
connection.subscriptionModelFlow.value = state.toMobileSubscriptionModel()
connection.connectionInfo.value = state.toMobileConnectionModel()
}
private fun processDisabledMobileState(state: MobileDisabled) {
@@ -229,47 +225,36 @@ constructor(
private fun subIdsString(): String =
_subscriptions.value.joinToString(",") { it.subscriptionId.toString() }
private fun Mobile.toMobileConnectionModel(): MobileConnectionModel {
return MobileConnectionModel(
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
isGsm = false, // TODO(b/261029387): not yet supported
cdmaLevel = level ?: 0,
primaryLevel = level ?: 0,
dataConnectionState =
DataConnectionState.Connected, // TODO(b/261029387): not yet supported
dataActivityDirection = activity,
carrierNetworkChangeActive = carrierNetworkChange,
resolvedNetworkType = dataType.toResolvedNetworkType()
)
}
private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType {
val key = mobileMappingsReverseLookup.value[this] ?: "dis"
return DefaultNetworkType(DEMO_NET_TYPE, key)
}
companion object {
private const val TAG = "DemoMobileConnectionsRepo"
private const val DEFAULT_SUB_ID = 1
private const val DEMO_NET_TYPE = 1234
}
}
private fun Mobile.toMobileSubscriptionModel(): MobileSubscriptionModel {
return MobileSubscriptionModel(
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
isGsm = false, // TODO(b/261029387): not yet supported
cdmaLevel = level ?: 0,
primaryLevel = level ?: 0,
dataConnectionState = DataConnectionState.Connected, // TODO(b/261029387): not yet supported
dataActivityDirection = activity,
carrierNetworkChangeActive = carrierNetworkChange,
// TODO(b/261185097): once mobile mappings can be mocked at this layer, we can build our
// own demo map
resolvedNetworkType = dataType.toResolvedNetworkType()
)
}
@Annotation.NetworkType
private fun SignalIcon.MobileIconGroup?.toNetworkType(): Int =
when (this) {
TelephonyIcons.THREE_G -> NETWORK_TYPE_GSM
TelephonyIcons.LTE -> NETWORK_TYPE_LTE
TelephonyIcons.FOUR_G -> NETWORK_TYPE_UMTS
TelephonyIcons.NR_5G -> NETWORK_TYPE_NR
TelephonyIcons.NR_5G_PLUS -> OVERRIDE_NETWORK_TYPE_NR_ADVANCED
else -> NETWORK_TYPE_UNKNOWN
}
private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType =
when (this) {
TelephonyIcons.NR_5G_PLUS -> OverrideNetworkType(toNetworkType())
else -> DefaultNetworkType(toNetworkType())
}
class DemoMobileConnectionRepository(val subId: Int) : MobileConnectionRepository {
override val subscriptionModelFlow = MutableStateFlow(MobileSubscriptionModel())
class DemoMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
override val connectionInfo = MutableStateFlow(MobileConnectionModel())
override val dataEnabled = MutableStateFlow(true)

View File

@@ -27,18 +27,20 @@ import android.telephony.TelephonyCallback
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
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.statusbar.pipeline.mobile.data.model.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
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.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
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.logOutputChange
import com.android.systemui.util.settings.GlobalSettings
import java.lang.IllegalStateException
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -58,11 +60,12 @@ import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
private val context: Context,
private val subId: Int,
override val subId: Int,
private val telephonyManager: TelephonyManager,
private val globalSettings: GlobalSettings,
defaultDataSubId: StateFlow<Int>,
globalMobileDataSettingChangedEvent: Flow<Unit>,
mobileMappingsProxy: MobileMappingsProxy,
bgDispatcher: CoroutineDispatcher,
logger: ConnectivityPipelineLogger,
scope: CoroutineScope,
@@ -78,8 +81,8 @@ class MobileConnectionRepositoryImpl(
private val telephonyCallbackEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
override val subscriptionModelFlow: StateFlow<MobileSubscriptionModel> = run {
var state = MobileSubscriptionModel()
override val connectionInfo: StateFlow<MobileConnectionModel> = run {
var state = MobileConnectionModel()
conflatedCallbackFlow {
// TODO (b/240569788): log all of these into the connectivity logger
val callback =
@@ -141,14 +144,27 @@ class MobileConnectionRepositoryImpl(
override fun onDisplayInfoChanged(
telephonyDisplayInfo: TelephonyDisplayInfo
) {
val networkType =
if (
if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) {
UnknownNetworkType
} else if (
telephonyDisplayInfo.overrideNetworkType ==
OVERRIDE_NETWORK_TYPE_NONE
) {
DefaultNetworkType(telephonyDisplayInfo.networkType)
DefaultNetworkType(
telephonyDisplayInfo.networkType,
mobileMappingsProxy.toIconKey(
telephonyDisplayInfo.networkType
)
)
} else {
OverrideNetworkType(telephonyDisplayInfo.overrideNetworkType)
OverrideNetworkType(
telephonyDisplayInfo.overrideNetworkType,
mobileMappingsProxy.toIconKeyOverride(
telephonyDisplayInfo.overrideNetworkType
)
)
}
state = state.copy(resolvedNetworkType = networkType)
trySend(state)
@@ -211,6 +227,7 @@ class MobileConnectionRepositoryImpl(
private val telephonyManager: TelephonyManager,
private val logger: ConnectivityPipelineLogger,
private val globalSettings: GlobalSettings,
private val mobileMappingsProxy: MobileMappingsProxy,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
) {
@@ -226,6 +243,7 @@ class MobileConnectionRepositoryImpl(
globalSettings,
defaultDataSubId,
globalMobileDataSettingChangedEvent,
mobileMappingsProxy,
bgDispatcher,
logger,
scope,

View File

@@ -36,6 +36,7 @@ import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -44,8 +45,10 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
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.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.util.settings.GlobalSettings
import javax.inject.Inject
@@ -59,6 +62,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
@@ -75,6 +79,7 @@ constructor(
private val subscriptionManager: SubscriptionManager,
private val telephonyManager: TelephonyManager,
private val logger: ConnectivityPipelineLogger,
mobileMappingsProxy: MobileMappingsProxy,
broadcastDispatcher: BroadcastDispatcher,
private val globalSettings: GlobalSettings,
private val context: Context,
@@ -82,14 +87,14 @@ constructor(
@Application private val scope: CoroutineScope,
private val mobileConnectionRepositoryFactory: MobileConnectionRepositoryImpl.Factory
) : MobileConnectionsRepository {
private val subIdRepositoryCache: MutableMap<Int, MobileConnectionRepository> = mutableMapOf()
private var subIdRepositoryCache: MutableMap<Int, MobileConnectionRepository> = mutableMapOf()
/**
* 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.
*/
override val subscriptionsFlow: StateFlow<List<SubscriptionInfo>> =
override val subscriptions: StateFlow<List<SubscriptionModel>> =
conflatedCallbackFlow {
val callback =
object : SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -105,7 +110,7 @@ constructor(
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
}
.mapLatest { fetchSubscriptionsList() }
.mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } }
.onEach { infos -> dropUnusedReposFromCache(infos) }
.stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
@@ -157,7 +162,7 @@ constructor(
*
* This flow will produce whenever the default data subscription or the carrier config changes.
*/
override val defaultDataSubRatConfig: StateFlow<Config> =
private val defaultDataSubRatConfig: StateFlow<Config> =
merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
.mapLatest { Config.readConfig(context) }
.stateIn(
@@ -166,6 +171,12 @@ constructor(
initialValue = Config.readConfig(context)
)
override val defaultMobileIconMapping: Flow<Map<String, MobileIconGroup>> =
defaultDataSubRatConfig.map { mobileMappingsProxy.mapIconSets(it) }
override val defaultMobileIconGroup: Flow<MobileIconGroup> =
defaultDataSubRatConfig.map { mobileMappingsProxy.getDefaultIcons(it) }
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
if (!isValidSubId(subId)) {
throw IllegalArgumentException(
@@ -229,7 +240,7 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
private fun isValidSubId(subId: Int): Boolean {
subscriptionsFlow.value.forEach {
subscriptions.value.forEach {
if (it.subscriptionId == subId) {
return true
}
@@ -248,18 +259,23 @@ constructor(
)
}
private fun dropUnusedReposFromCache(newInfos: List<SubscriptionInfo>) {
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
val currentValidSubscriptionIds = newInfos.map { it.subscriptionId }
subIdRepositoryCache.keys.forEach {
if (!currentValidSubscriptionIds.contains(it)) {
subIdRepositoryCache.remove(it)
}
}
subIdRepositoryCache =
subIdRepositoryCache
.filter { currentValidSubscriptionIds.contains(it.key) }
.toMutableMap()
}
private suspend fun fetchSubscriptionsList(): List<SubscriptionInfo> =
withContext(bgDispatcher) { subscriptionManager.completeActiveSubscriptionInfoList }
private fun SubscriptionInfo.toSubscriptionModel(): SubscriptionModel =
SubscriptionModel(
subscriptionId = subscriptionId,
isOpportunistic = isOpportunistic,
)
}

View File

@@ -20,10 +20,7 @@ import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.util.CarrierConfigTracker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -70,10 +67,9 @@ class MobileIconInteractorImpl(
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
override val isDefaultConnectionFailed: StateFlow<Boolean>,
mobileMappingsProxy: MobileMappingsProxy,
connectionRepository: MobileConnectionRepository,
) : MobileIconInteractor {
private val mobileStatusInfo = connectionRepository.subscriptionModelFlow
private val connectionInfo = connectionRepository.connectionInfo
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
@@ -82,33 +78,27 @@ class MobileIconInteractorImpl(
/** Observable for the current RAT indicator icon ([MobileIconGroup]) */
override val networkTypeIconGroup: StateFlow<MobileIconGroup> =
combine(
mobileStatusInfo,
connectionInfo,
defaultMobileIconMapping,
defaultMobileIconGroup,
) { info, mapping, defaultGroup ->
val lookupKey =
when (val resolved = info.resolvedNetworkType) {
is DefaultNetworkType -> mobileMappingsProxy.toIconKey(resolved.type)
is OverrideNetworkType ->
mobileMappingsProxy.toIconKeyOverride(resolved.type)
}
mapping[lookupKey] ?: defaultGroup
mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
}
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
override val isEmergencyOnly: StateFlow<Boolean> =
mobileStatusInfo
connectionInfo
.mapLatest { it.isEmergencyOnly }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val level: StateFlow<Int> =
mobileStatusInfo
.mapLatest { mobileModel ->
connectionInfo
.mapLatest { connection ->
// TODO: incorporate [MobileMappings.Config.alwaysShowCdmaRssi]
if (mobileModel.isGsm) {
mobileModel.primaryLevel
if (connection.isGsm) {
connection.primaryLevel
} else {
mobileModel.cdmaLevel
connection.cdmaLevel
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
@@ -120,7 +110,7 @@ class MobileIconInteractorImpl(
override val numberOfLevels: StateFlow<Int> = MutableStateFlow(4)
override val isDataConnected: StateFlow<Boolean> =
mobileStatusInfo
.mapLatest { subscriptionModel -> subscriptionModel.dataConnectionState == Connected }
connectionInfo
.mapLatest { connection -> connection.dataConnectionState == Connected }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
}

View File

@@ -17,17 +17,16 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.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.data.repository.UserSetupRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.util.CarrierConfigTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -53,7 +52,7 @@ import kotlinx.coroutines.flow.stateIn
*/
interface MobileIconsInteractor {
/** List of subscriptions, potentially filtered for CBRS */
val filteredSubscriptions: Flow<List<SubscriptionInfo>>
val filteredSubscriptions: Flow<List<SubscriptionModel>>
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
@@ -79,7 +78,6 @@ class MobileIconsInteractorImpl
constructor(
private val mobileConnectionsRepo: MobileConnectionsRepository,
private val carrierConfigTracker: CarrierConfigTracker,
private val mobileMappingsProxy: MobileMappingsProxy,
userSetupRepo: UserSetupRepository,
@Application private val scope: CoroutineScope,
) : MobileIconsInteractor {
@@ -102,8 +100,8 @@ constructor(
.flatMapLatest { it?.dataEnabled ?: flowOf(false) }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
private val unfilteredSubscriptions: Flow<List<SubscriptionInfo>> =
mobileConnectionsRepo.subscriptionsFlow
private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
mobileConnectionsRepo.subscriptions
/**
* Generally, SystemUI wants to show iconography for each subscription that is listed by
@@ -118,7 +116,7 @@ constructor(
* [CarrierConfigManager.KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN],
* and by checking which subscription is opportunistic, or which one is active.
*/
override val filteredSubscriptions: Flow<List<SubscriptionInfo>> =
override val filteredSubscriptions: Flow<List<SubscriptionModel>> =
combine(unfilteredSubscriptions, activeMobileDataSubscriptionId) { unfilteredSubs, activeId
->
// Based on the old logic,
@@ -154,15 +152,19 @@ constructor(
* subscription Id. This mapping is the same for every subscription.
*/
override val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>> =
mobileConnectionsRepo.defaultDataSubRatConfig
.mapLatest { mobileMappingsProxy.mapIconSets(it) }
.stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = mapOf())
mobileConnectionsRepo.defaultMobileIconMapping.stateIn(
scope,
SharingStarted.WhileSubscribed(),
initialValue = mapOf()
)
/** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
mobileConnectionsRepo.defaultDataSubRatConfig
.mapLatest { mobileMappingsProxy.getDefaultIcons(it) }
.stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = TelephonyIcons.G)
mobileConnectionsRepo.defaultMobileIconGroup.stateIn(
scope,
SharingStarted.WhileSubscribed(),
initialValue = TelephonyIcons.G
)
/**
* We want to show an error state when cellular has actually failed to validate, but not if some
@@ -189,7 +191,6 @@ constructor(
defaultMobileIconMapping,
defaultMobileIconGroup,
isDefaultConnectionFailed,
mobileMappingsProxy,
mobileConnectionsRepo.getRepoForSubId(subId),
)
}

View File

@@ -56,8 +56,8 @@ constructor(
private val statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable {
private val mobileSubIds: Flow<List<Int>> =
interactor.filteredSubscriptions.mapLatest { infos ->
infos.map { subscriptionInfo -> subscriptionInfo.subscriptionId }
interactor.filteredSubscriptions.mapLatest { subscriptions ->
subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
}
/**

View File

@@ -32,6 +32,8 @@ class ModernStatusBarMobileView(
attrs: AttributeSet?,
) : BaseStatusBarFrameLayout(context, attrs) {
var subId: Int = -1
private lateinit var slot: String
override fun getSlot() = slot
@@ -76,6 +78,7 @@ class ModernStatusBarMobileView(
as ModernStatusBarMobileView)
.also {
it.slot = slot
it.subId = viewModel.subscriptionId
MobileIconBinder.bind(it, viewModel)
}
}

View File

@@ -23,7 +23,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMob
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
* View model for describing the system's current mobile cellular connections. The result is a list
@@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.Flow
class MobileIconsViewModel
@Inject
constructor(
val subscriptionIdsFlow: Flow<List<Int>>,
val subscriptionIdsFlow: StateFlow<List<Int>>,
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
) {
@@ -51,7 +51,7 @@ constructor(
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
) {
fun create(subscriptionIdsFlow: Flow<List<Int>>): MobileIconsViewModel {
fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
return MobileIconsViewModel(
subscriptionIdsFlow,
interactor,

View File

@@ -16,12 +16,13 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileConnectionRepository : MobileConnectionRepository {
private val _subscriptionsModelFlow = MutableStateFlow(MobileSubscriptionModel())
override val subscriptionModelFlow = _subscriptionsModelFlow
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionRepository
class FakeMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
private val _connectionInfo = MutableStateFlow(MobileConnectionModel())
override val connectionInfo = _connectionInfo
private val _dataEnabled = MutableStateFlow(true)
override val dataEnabled = _dataEnabled
@@ -29,8 +30,8 @@ class FakeMobileConnectionRepository : MobileConnectionRepository {
private val _isDefaultDataSubscription = MutableStateFlow(true)
override val isDefaultDataSubscription = _isDefaultDataSubscription
fun setMobileSubscriptionModel(model: MobileSubscriptionModel) {
_subscriptionsModelFlow.value = model
fun setConnectionInfo(model: MobileConnectionModel) {
_connectionInfo.value = model
}
fun setDataEnabled(enabled: Boolean) {

View File

@@ -16,23 +16,43 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settingslib.mobile.MobileMappings.Config
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import kotlinx.coroutines.flow.Flow
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileConnectionsRepository : MobileConnectionsRepository {
private val _subscriptionsFlow = MutableStateFlow<List<SubscriptionInfo>>(listOf())
override val subscriptionsFlow: Flow<List<SubscriptionInfo>> = _subscriptionsFlow
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
class FakeMobileConnectionsRepository(mobileMappings: MobileMappingsProxy) :
MobileConnectionsRepository {
val GSM_KEY = mobileMappings.toIconKey(GSM)
val LTE_KEY = mobileMappings.toIconKey(LTE)
val UMTS_KEY = mobileMappings.toIconKey(UMTS)
val LTE_ADVANCED_KEY = mobileMappings.toIconKeyOverride(LTE_ADVANCED_PRO)
/**
* To avoid a reliance on [MobileMappings], we'll build a simpler map from network type to
* mobile icon. See TelephonyManager.NETWORK_TYPES for a list of types and [TelephonyIcons] for
* the exhaustive set of icons
*/
val TEST_MAPPING: Map<String, SignalIcon.MobileIconGroup> =
mapOf(
GSM_KEY to TelephonyIcons.THREE_G,
LTE_KEY to TelephonyIcons.LTE,
UMTS_KEY to TelephonyIcons.FOUR_G,
LTE_ADVANCED_KEY to TelephonyIcons.NR_5G,
)
private val _subscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
override val subscriptions = _subscriptions
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
private val _defaultDataSubRatConfig = MutableStateFlow(Config())
override val defaultDataSubRatConfig = _defaultDataSubRatConfig
private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
override val defaultDataSubId = _defaultDataSubId
@@ -41,18 +61,21 @@ class FakeMobileConnectionsRepository : MobileConnectionsRepository {
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId] ?: FakeMobileConnectionRepository().also { subIdRepos[subId] = it }
return subIdRepos[subId]
?: FakeMobileConnectionRepository(subId).also { subIdRepos[subId] = it }
}
private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
override val globalMobileDataSettingChangedEvent = _globalMobileDataSettingChangedEvent
fun setSubscriptions(subs: List<SubscriptionInfo>) {
_subscriptionsFlow.value = subs
}
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
override val defaultMobileIconMapping = _defaultMobileIconMapping
fun setDefaultDataSubRatConfig(config: Config) {
_defaultDataSubRatConfig.value = config
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
fun setSubscriptions(subs: List<SubscriptionModel>) {
_subscriptions.value = subs
}
fun setDefaultDataSubId(id: Int) {
@@ -74,4 +97,14 @@ class FakeMobileConnectionsRepository : MobileConnectionsRepository {
fun setMobileConnectionRepositoryMap(connections: Map<Int, MobileConnectionRepository>) {
connections.forEach { entry -> subIdRepos[entry.key] = entry.value }
}
companion object {
val DEFAULT_ICON = TelephonyIcons.G
// Use [MobileMappings] to define some simple definitions
const val GSM = TelephonyManager.NETWORK_TYPE_GSM
const val LTE = TelephonyManager.NETWORK_TYPE_LTE
const val UMTS = TelephonyManager.NETWORK_TYPE_UMTS
const val LTE_ADVANCED_PRO = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO
}
}

View File

@@ -24,11 +24,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoModeMobileConnectionDataSource
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
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.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
@@ -76,6 +78,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
private val globalSettings = FakeSettings()
private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val mobileMappings = FakeMobileMappingsProxy()
private val scope = CoroutineScope(IMMEDIATE)
@@ -97,6 +100,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
subscriptionManager,
telephonyManager,
logger,
mobileMappings,
fakeBroadcastDispatcher,
globalSettings,
context,
@@ -155,15 +159,15 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
// The real subscriptions has 2 subs
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2))
// Demo mode turns on, and we should see only the demo subscriptions
startDemoMode()
@@ -176,7 +180,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
finishDemoMode()
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2))
job.cancel()
}
@@ -211,9 +215,11 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
private const val SUB_2_ID = 2
private val SUB_2 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
}
}

View File

@@ -23,7 +23,7 @@ import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -109,18 +109,18 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
) {
when (model) {
is FakeNetworkEventModel.Mobile -> {
val subscriptionModel: MobileSubscriptionModel = conn.subscriptionModelFlow.value
val connectionInfo: MobileConnectionModel = conn.connectionInfo.value
assertThat(conn.subId).isEqualTo(model.subId)
assertThat(subscriptionModel.cdmaLevel).isEqualTo(model.level)
assertThat(subscriptionModel.primaryLevel).isEqualTo(model.level)
assertThat(subscriptionModel.dataActivityDirection).isEqualTo(model.activity)
assertThat(subscriptionModel.carrierNetworkChangeActive)
assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
assertThat(connectionInfo.carrierNetworkChangeActive)
.isEqualTo(model.carrierNetworkChange)
// TODO(b/261029387): check these once we start handling them
assertThat(subscriptionModel.isEmergencyOnly).isFalse()
assertThat(subscriptionModel.isGsm).isFalse()
assertThat(subscriptionModel.dataConnectionState)
assertThat(connectionInfo.isEmergencyOnly).isFalse()
assertThat(connectionInfo.isGsm).isFalse()
assertThat(connectionInfo.dataConnectionState)
.isEqualTo(DataConnectionState.Connected)
}
// MobileDisabled isn't combinatorial in nature, and is tested in

View File

@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import androidx.test.filters.SmallTest
@@ -24,7 +23,8 @@ import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
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.util.mockito.mock
@@ -73,8 +73,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `network event - create new subscription`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
assertThat(latest).isEmpty()
@@ -89,8 +89,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `network event - reuses subscription when same Id`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
assertThat(latest).isEmpty()
@@ -111,8 +111,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `multiple subscriptions`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
fakeNetworkEventFlow.value = validMobileEvent(subId = 1)
fakeNetworkEventFlow.value = validMobileEvent(subId = 2)
@@ -125,8 +125,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `mobile disabled event - disables connection - subId specified - single conn`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
fakeNetworkEventFlow.value = validMobileEvent(subId = 1, level = 1)
@@ -140,8 +140,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `mobile disabled event - disables connection - subId not specified - single conn`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
fakeNetworkEventFlow.value = validMobileEvent(subId = 1, level = 1)
@@ -155,8 +155,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `mobile disabled event - disables connection - subId specified - multiple conn`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
fakeNetworkEventFlow.value = validMobileEvent(subId = 1, level = 1)
fakeNetworkEventFlow.value = validMobileEvent(subId = 2, level = 1)
@@ -171,8 +171,8 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun `mobile disabled event - subId not specified - multiple conn - ignores command`() =
testScope.runTest {
var latest: List<SubscriptionInfo>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
fakeNetworkEventFlow.value = validMobileEvent(subId = 1, level = 1)
fakeNetworkEventFlow.value = validMobileEvent(subId = 2, level = 1)
@@ -184,13 +184,32 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel()
}
/** Regression test for b/261706421 */
@Test
fun `multiple connections - remove all - does not throw`() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
// Two subscriptions are added
fakeNetworkEventFlow.value = validMobileEvent(subId = 1, level = 1)
fakeNetworkEventFlow.value = validMobileEvent(subId = 2, level = 1)
// Then both are removed by turning off demo mode
underTest.stopProcessingCommands()
assertThat(latest).isEmpty()
job.cancel()
}
@Test
fun `demo connection - single subscription`() =
testScope.runTest {
var currentEvent: FakeNetworkEventModel = validMobileEvent(subId = 1)
var connections: List<DemoMobileConnectionRepository>? = null
val job =
underTest.subscriptionsFlow
underTest.subscriptions
.onEach { infos ->
connections =
infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
@@ -222,7 +241,7 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
var connection2: DemoMobileConnectionRepository? = null
var connections: List<DemoMobileConnectionRepository>? = null
val job =
underTest.subscriptionsFlow
underTest.subscriptions
.onEach { infos ->
connections =
infos.map { info -> underTest.getRepoForSubId(info.subscriptionId) }
@@ -266,18 +285,18 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
) {
when (model) {
is FakeNetworkEventModel.Mobile -> {
val subscriptionModel: MobileSubscriptionModel = conn.subscriptionModelFlow.value
val connectionInfo: MobileConnectionModel = conn.connectionInfo.value
assertThat(conn.subId).isEqualTo(model.subId)
assertThat(subscriptionModel.cdmaLevel).isEqualTo(model.level)
assertThat(subscriptionModel.primaryLevel).isEqualTo(model.level)
assertThat(subscriptionModel.dataActivityDirection).isEqualTo(model.activity)
assertThat(subscriptionModel.carrierNetworkChangeActive)
assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
assertThat(connectionInfo.carrierNetworkChangeActive)
.isEqualTo(model.carrierNetworkChange)
// TODO(b/261029387) check these once we start handling them
assertThat(subscriptionModel.isEmergencyOnly).isFalse()
assertThat(subscriptionModel.isGsm).isFalse()
assertThat(subscriptionModel.dataConnectionState)
assertThat(connectionInfo.isEmergencyOnly).isFalse()
assertThat(connectionInfo.isGsm).isFalse()
assertThat(connectionInfo.dataConnectionState)
.isEqualTo(DataConnectionState.Connected)
}
else -> {}

View File

@@ -37,10 +37,12 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
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.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -72,8 +74,9 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Mock private lateinit var logger: ConnectivityPipelineLogger
private val scope = CoroutineScope(IMMEDIATE)
private val mobileMappings = FakeMobileMappingsProxy()
private val globalSettings = FakeSettings()
private val connectionsRepo = FakeMobileConnectionsRepository()
private val connectionsRepo = FakeMobileConnectionsRepository(mobileMappings)
@Before
fun setUp() {
@@ -89,6 +92,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
globalSettings,
connectionsRepo.defaultDataSubId,
connectionsRepo.globalMobileDataSettingChangedEvent,
mobileMappings,
IMMEDIATE,
logger,
scope,
@@ -103,10 +107,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_default() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(MobileSubscriptionModel())
assertThat(latest).isEqualTo(MobileConnectionModel())
job.cancel()
}
@@ -114,8 +118,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_emergencyOnly() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val serviceState = ServiceState()
serviceState.isEmergencyOnly = true
@@ -130,8 +134,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_emergencyOnly_toggles() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<ServiceStateListener>()
val serviceState = ServiceState()
@@ -148,8 +152,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_signalStrengths_levelsUpdate() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>()
val strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = true)
@@ -165,8 +169,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataConnectionState_connected() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback =
getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
@@ -180,8 +184,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataConnectionState_connecting() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback =
getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
@@ -195,8 +199,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataConnectionState_disconnected() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback =
getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
@@ -210,8 +214,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataConnectionState_disconnecting() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback =
getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
@@ -225,8 +229,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataConnectionState_unknown() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback =
getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
@@ -240,8 +244,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_dataActivity() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DataActivityListener>()
callback.onDataActivity(3)
@@ -254,8 +258,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun testFlowForSubId_carrierNetworkChange() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.CarrierNetworkListener>()
callback.onCarrierNetworkChange(true)
@@ -268,11 +272,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun subscriptionFlow_networkType_default() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val type = NETWORK_TYPE_UNKNOWN
val expected = DefaultNetworkType(type)
val expected = UnknownNetworkType
assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
@@ -282,12 +286,12 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun subscriptionFlow_networkType_updatesUsingDefault() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val type = NETWORK_TYPE_LTE
val expected = DefaultNetworkType(type)
val expected = DefaultNetworkType(type, mobileMappings.toIconKey(type))
val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
callback.onDisplayInfoChanged(ti)
@@ -299,14 +303,15 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun subscriptionFlow_networkType_updatesUsingOverride() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
val expected = OverrideNetworkType(type)
val expected = OverrideNetworkType(type, mobileMappings.toIconKeyOverride(type))
val ti =
mock<TelephonyDisplayInfo>().also {
whenever(it.networkType).thenReturn(type)
whenever(it.overrideNetworkType).thenReturn(type)
}
callback.onDisplayInfoChanged(ti)

View File

@@ -32,6 +32,8 @@ import androidx.test.filters.SmallTest
import com.android.internal.telephony.PhoneConstants
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -50,6 +52,7 @@ import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -65,6 +68,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: ConnectivityPipelineLogger
private val mobileMappings = FakeMobileMappingsProxy()
private val scope = CoroutineScope(IMMEDIATE)
private val globalSettings = FakeSettings()
@@ -72,18 +77,37 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
// Set up so the individual connection repositories
whenever(telephonyManager.createForSubscriptionId(anyInt())).thenAnswer { invocation ->
telephonyManager.also {
whenever(telephonyManager.subscriptionId).thenReturn(invocation.getArgument(0))
}
}
val connectionFactory: MobileConnectionRepositoryImpl.Factory =
MobileConnectionRepositoryImpl.Factory(
context = context,
telephonyManager = telephonyManager,
bgDispatcher = IMMEDIATE,
globalSettings = globalSettings,
logger = logger,
mobileMappingsProxy = mobileMappings,
scope = scope,
)
underTest =
MobileConnectionsRepositoryImpl(
connectivityManager,
subscriptionManager,
telephonyManager,
logger,
mobileMappings,
fakeBroadcastDispatcher,
globalSettings,
context,
IMMEDIATE,
scope,
mock(),
connectionFactory,
)
}
@@ -95,21 +119,21 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun testSubscriptions_initiallyEmpty() =
runBlocking(IMMEDIATE) {
assertThat(underTest.subscriptionsFlow.value).isEqualTo(listOf<SubscriptionInfo>())
assertThat(underTest.subscriptions.value).isEqualTo(listOf<SubscriptionModel>())
}
@Test
fun testSubscriptions_listUpdates() =
runBlocking(IMMEDIATE) {
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2))
job.cancel()
}
@@ -117,9 +141,9 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun testSubscriptions_removingSub_updatesList() =
runBlocking(IMMEDIATE) {
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptionsFlow.onEach { latest = it }.launchIn(this)
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
// WHEN 2 networks show up
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
@@ -132,7 +156,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
getSubscriptionCallback().onSubscriptionsChanged()
// THEN the subscriptions list represents the newest change
assertThat(latest).isEqualTo(listOf(SUB_2))
assertThat(latest).isEqualTo(listOf(MODEL_2))
job.cancel()
}
@@ -162,7 +186,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun testConnectionRepository_validSubId_isCached() =
runBlocking(IMMEDIATE) {
val job = underTest.subscriptionsFlow.launchIn(this)
val job = underTest.subscriptions.launchIn(this)
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1))
@@ -179,7 +203,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun testConnectionCache_clearsInvalidSubscriptions() =
runBlocking(IMMEDIATE) {
val job = underTest.subscriptionsFlow.launchIn(this)
val job = underTest.subscriptions.launchIn(this)
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
@@ -202,10 +226,36 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel()
}
/** Regression test for b/261706421 */
@Test
fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
runBlocking(IMMEDIATE) {
val job = underTest.subscriptions.launchIn(this)
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
// Get repos to trigger caching
val repo1 = underTest.getRepoForSubId(SUB_1_ID)
val repo2 = underTest.getRepoForSubId(SUB_2_ID)
assertThat(underTest.getSubIdRepoCache())
.containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
// All subscriptions disappear
whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
getSubscriptionCallback().onSubscriptionsChanged()
assertThat(underTest.getSubIdRepoCache()).isEmpty()
job.cancel()
}
@Test
fun testConnectionRepository_invalidSubId_throws() =
runBlocking(IMMEDIATE) {
val job = underTest.subscriptionsFlow.launchIn(this)
val job = underTest.subscriptions.launchIn(this)
assertThrows(IllegalArgumentException::class.java) {
underTest.getRepoForSubId(SUB_1_ID)
@@ -371,10 +421,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
private const val SUB_2_ID = 2
private val SUB_2 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
private const val NET_ID = 123
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }

View File

@@ -16,14 +16,15 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO
import android.telephony.TelephonyManager.NETWORK_TYPE_GSM
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIconsInteractor {
@@ -47,8 +48,8 @@ class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIco
override val isDefaultConnectionFailed = MutableStateFlow(false)
private val _filteredSubscriptions = MutableStateFlow<List<SubscriptionInfo>>(listOf())
override val filteredSubscriptions = _filteredSubscriptions
private val _filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
override val filteredSubscriptions: Flow<List<SubscriptionModel>> = _filteredSubscriptions
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled

View File

@@ -24,9 +24,9 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
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
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
@@ -49,7 +49,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconInteractor
private val mobileMappingsProxy = FakeMobileMappingsProxy()
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy)
private val connectionRepository = FakeMobileConnectionRepository()
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID)
private val scope = CoroutineScope(IMMEDIATE)
@@ -62,7 +62,6 @@ class MobileIconInteractorTest : SysuiTestCase() {
mobileIconsInteractor.defaultMobileIconMapping,
mobileIconsInteractor.defaultMobileIconGroup,
mobileIconsInteractor.isDefaultConnectionFailed,
mobileMappingsProxy,
connectionRepository,
)
}
@@ -70,8 +69,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun gsm_level_default_unknown() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(isGsm = true),
connectionRepository.setConnectionInfo(
MobileConnectionModel(isGsm = true),
)
var latest: Int? = null
@@ -85,8 +84,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun gsm_usesGsmLevel() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(
connectionRepository.setConnectionInfo(
MobileConnectionModel(
isGsm = true,
primaryLevel = GSM_LEVEL,
cdmaLevel = CDMA_LEVEL
@@ -104,8 +103,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun cdma_level_default_unknown() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(isGsm = false),
connectionRepository.setConnectionInfo(
MobileConnectionModel(isGsm = false),
)
var latest: Int? = null
@@ -118,8 +117,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun cdma_usesCdmaLevel() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(
connectionRepository.setConnectionInfo(
MobileConnectionModel(
isGsm = false,
primaryLevel = GSM_LEVEL,
cdmaLevel = CDMA_LEVEL
@@ -137,8 +136,11 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun iconGroup_three_g() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(resolvedNetworkType = DefaultNetworkType(THREE_G)),
connectionRepository.setConnectionInfo(
MobileConnectionModel(
resolvedNetworkType =
DefaultNetworkType(THREE_G, mobileMappingsProxy.toIconKey(THREE_G))
),
)
var latest: MobileIconGroup? = null
@@ -152,16 +154,23 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun iconGroup_updates_on_change() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(resolvedNetworkType = DefaultNetworkType(THREE_G)),
connectionRepository.setConnectionInfo(
MobileConnectionModel(
resolvedNetworkType =
DefaultNetworkType(THREE_G, mobileMappingsProxy.toIconKey(THREE_G))
),
)
var latest: MobileIconGroup? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(
resolvedNetworkType = DefaultNetworkType(FOUR_G),
connectionRepository.setConnectionInfo(
MobileConnectionModel(
resolvedNetworkType =
DefaultNetworkType(
FOUR_G,
mobileMappingsProxy.toIconKey(FOUR_G),
),
),
)
yield()
@@ -174,8 +183,14 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun iconGroup_5g_override_type() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(resolvedNetworkType = OverrideNetworkType(FIVE_G_OVERRIDE)),
connectionRepository.setConnectionInfo(
MobileConnectionModel(
resolvedNetworkType =
OverrideNetworkType(
FIVE_G_OVERRIDE,
mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE)
)
),
)
var latest: MobileIconGroup? = null
@@ -189,9 +204,13 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun iconGroup_default_if_no_lookup() =
runBlocking(IMMEDIATE) {
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(
resolvedNetworkType = DefaultNetworkType(NETWORK_TYPE_UNKNOWN),
connectionRepository.setConnectionInfo(
MobileConnectionModel(
resolvedNetworkType =
DefaultNetworkType(
NETWORK_TYPE_UNKNOWN,
mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN)
),
),
)
@@ -238,8 +257,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
var latest: Boolean? = null
val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(dataConnectionState = DataConnectionState.Connected)
connectionRepository.setConnectionInfo(
MobileConnectionModel(dataConnectionState = DataConnectionState.Connected)
)
yield()
@@ -254,8 +273,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
var latest: Boolean? = null
val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
connectionRepository.setMobileSubscriptionModel(
MobileSubscriptionModel(dataConnectionState = DataConnectionState.Disconnected)
connectionRepository.setConnectionInfo(
MobileConnectionModel(dataConnectionState = DataConnectionState.Disconnected)
)
assertThat(latest).isFalse()

View File

@@ -16,17 +16,16 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.util.CarrierConfigTracker
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
@@ -45,8 +44,8 @@ import org.mockito.MockitoAnnotations
class MobileIconsInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconsInteractor
private val userSetupRepository = FakeUserSetupRepository()
private val connectionsRepository = FakeMobileConnectionsRepository()
private val mobileMappingsProxy = FakeMobileMappingsProxy()
private val connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy)
private val scope = CoroutineScope(IMMEDIATE)
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
@@ -69,7 +68,6 @@ class MobileIconsInteractorTest : SysuiTestCase() {
MobileIconsInteractorImpl(
connectionsRepository,
carrierConfigTracker,
mobileMappingsProxy,
userSetupRepository,
scope
)
@@ -80,10 +78,10 @@ class MobileIconsInteractorTest : SysuiTestCase() {
@Test
fun filteredSubscriptions_default() =
runBlocking(IMMEDIATE) {
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(listOf<SubscriptionInfo>())
assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
job.cancel()
}
@@ -93,7 +91,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
runBlocking(IMMEDIATE) {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
@@ -109,7 +107,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
@@ -126,7 +124,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
@@ -143,7 +141,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
@@ -161,7 +159,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
var latest: List<SubscriptionInfo>? = null
var latest: List<SubscriptionModel>? = null
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
@@ -261,29 +259,19 @@ class MobileIconsInteractorTest : SysuiTestCase() {
private val IMMEDIATE = Dispatchers.Main.immediate
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val CONNECTION_1 = FakeMobileConnectionRepository()
private val SUB_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID)
private const val SUB_2_ID = 2
private val SUB_2 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
private val CONNECTION_2 = FakeMobileConnectionRepository()
private val SUB_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID)
private const val SUB_3_ID = 3
private val SUB_3_OPP =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_3_ID)
whenever(it.isOpportunistic).thenReturn(true)
}
private val CONNECTION_3 = FakeMobileConnectionRepository()
private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID)
private const val SUB_4_ID = 4
private val SUB_4_OPP =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_4_ID)
whenever(it.isOpportunistic).thenReturn(true)
}
private val CONNECTION_4 = FakeMobileConnectionRepository()
private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID)
}
}