From 6acee7b1eeaa421267accd62cd08f8af5eb163a6 Mon Sep 17 00:00:00 2001 From: Evan Laird Date: Thu, 8 Dec 2022 15:58:31 -0500 Subject: [PATCH] [Sb refactor] Support NetworkName in new mobile pipeline The network name can come from one of 3 places: 1. TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED, which passes an intent containing a network name which we can derive 2. ServiceState.getOperatorAlphaShort(), from the service state changed telephony callback 3. A default value, set in the framework config `lockscreen_carrier_default` Based on the implementation that currently lives in `MobileSignalController`, we were only using `NetworkName` a.k.a. `EXTRA_SPN`, and not `NetworkNameData` a.k.a. `EXTRA_DATA_SPN`. Therefore this CL only adds support for the former. The front end does not consume this piece of data yet, since it is only needed for QuickSettings. Future CLs will add support for a LocationBasedMobileViewModel (similar to Wi-Fi), which can then start consuming the values. Test: MobileConnectionRepositoryTest Test: DemoMobileConnectionsRepositoryTest Test: MobileIconInteractorTest Bug: 238425913 Change-Id: I04fa90bf7b52aa992f9721c7301f541f6085cf68 --- .../data/model/MobileConnectionModel.kt | 23 ++-- .../mobile/data/model/NetworkNameModel.kt | 62 +++++++++++ .../repository/MobileConnectionRepository.kt | 4 + .../demo/DemoMobileConnectionsRepository.kt | 4 + .../DemoModeMobileConnectionDataSource.kt | 2 + .../demo/model/FakeNetworkEventModel.kt | 1 + .../prod/MobileConnectionRepositoryImpl.kt | 29 +++++ .../prod/MobileConnectionsRepositoryImpl.kt | 12 +++ .../domain/interactor/MobileIconInteractor.kt | 26 +++++ .../FakeMobileConnectionRepository.kt | 4 + .../DemoMobileConnectionParameterizedTest.kt | 14 ++- .../DemoMobileConnectionsRepositoryTest.kt | 3 + .../prod/MobileConnectionRepositoryTest.kt | 100 ++++++++++++++++++ .../prod/MobileConnectionsRepositoryTest.kt | 1 + .../interactor/FakeMobileIconInteractor.kt | 3 + .../interactor/MobileIconInteractorTest.kt | 39 +++++++ 16 files changed, 318 insertions(+), 9 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt index e1e9540e37f0a..6c37f94007cba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt @@ -39,33 +39,42 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityMod * threading complex system objects through the pipeline. */ data class MobileConnectionModel( - /** From [ServiceStateListener.onServiceStateChanged] */ + /** Fields below are from [ServiceStateListener.onServiceStateChanged] */ val isEmergencyOnly: Boolean = false, val isRoaming: Boolean = false, + /** + * See [android.telephony.ServiceState.getOperatorAlphaShort], this value is defined as the + * current registered operator name in short alphanumeric format. In some cases this name might + * be preferred over other methods of calculating the network name + */ + val operatorAlphaShort: String? = null, - /** From [SignalStrengthsListener.onSignalStrengthsChanged] */ + /** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */ val isGsm: Boolean = false, @IntRange(from = 0, to = 4) val cdmaLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, @IntRange(from = 0, to = 4) val primaryLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, - /** Mapped from [DataConnectionStateListener.onDataConnectionStateChanged] */ + /** Fields below from [DataConnectionStateListener.onDataConnectionStateChanged] */ val dataConnectionState: DataConnectionState = Disconnected, - /** From [DataActivityListener.onDataActivity]. See [TelephonyManager] for the values */ + /** + * Fields below from [DataActivityListener.onDataActivity]. See [TelephonyManager] for the + * values + */ val dataActivityDirection: DataActivityModel = DataActivityModel( hasActivityIn = false, hasActivityOut = false, ), - /** From [CarrierNetworkListener.onCarrierNetworkChange] */ + /** Fields below from [CarrierNetworkListener.onCarrierNetworkChange] */ val carrierNetworkChangeActive: Boolean = false, + /** Fields below from [DisplayInfoListener.onDisplayInfoChanged]. */ + /** - * From [DisplayInfoListener.onDisplayInfoChanged]. - * * [resolvedNetworkType] is the [TelephonyDisplayInfo.getOverrideNetworkType] if it exists or * [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt new file mode 100644 index 0000000000000..a8cf35ad30298 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt @@ -0,0 +1,62 @@ +/* + * 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 + +import android.content.Intent +import android.telephony.TelephonyManager.EXTRA_DATA_SPN +import android.telephony.TelephonyManager.EXTRA_PLMN +import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN +import android.telephony.TelephonyManager.EXTRA_SHOW_SPN + +/** + * Encapsulates the data needed to show a network name for a mobile network. The data is parsed from + * the intent sent by [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED]. + */ +sealed interface NetworkNameModel { + val name: String + + /** The default name is read from [com.android.internal.R.string.lockscreen_carrier_default] */ + data class Default(override val name: String) : NetworkNameModel + + /** + * This name has been derived from telephony intents. see + * [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED] + */ + data class Derived(override val name: String) : NetworkNameModel +} + +fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? { + val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false) + val spn = getStringExtra(EXTRA_DATA_SPN) + val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false) + val plmn = getStringExtra(EXTRA_PLMN) + + val str = StringBuilder() + val strData = StringBuilder() + if (showPlmn && plmn != null) { + str.append(plmn) + strData.append(plmn) + } + if (showSpn && spn != null) { + if (str.isNotEmpty()) { + str.append(separator) + } + str.append(spn) + } + + return if (str.isNotEmpty()) NetworkNameModel.Derived(str.toString()) else null +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt index fc59f6e1eccf0..2fd415e6777f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt @@ -21,6 +21,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -58,4 +59,7 @@ interface MobileConnectionRepository { * True if the Enhanced Roaming Indicator (ERI) display number is not [TelephonyManager.ERI_OFF] */ val cdmaRoaming: StateFlow + + /** The service provider name for this network connection, or the default name */ + val networkName: StateFlow } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt index 44eeb7ca24222..d3ee85f19347c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt @@ -27,6 +27,7 @@ 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.MobileConnectionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel @@ -187,6 +188,7 @@ constructor( // This is always true here, because we split out disabled states at the data-source level connection.dataEnabled.value = true connection.isDefaultDataSubscription.value = state.dataType != null + connection.networkName.value = NetworkNameModel.Derived(state.name) connection.cdmaRoaming.value = state.roaming connection.connectionInfo.value = state.toMobileConnectionModel() @@ -266,4 +268,6 @@ class DemoMobileConnectionRepository(override val subId: Int) : MobileConnection override val isDefaultDataSubscription = MutableStateFlow(true) override val cdmaRoaming = MutableStateFlow(false) + + override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo network")) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt index 2cdbc191f7ad8..a1ae8ed7329a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt @@ -99,6 +99,7 @@ constructor( val activity = getString("activity")?.toActivity() val carrierNetworkChange = getString("carriernetworkchange") == "show" val roaming = getString("roam") == "show" + val name = getString("networkname") ?: "demo mode" return Mobile( level = level, @@ -109,6 +110,7 @@ constructor( activity = activity, carrierNetworkChange = carrierNetworkChange, roaming = roaming, + name = name, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt index b8543eca69a85..8b03f71a27293 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt @@ -35,6 +35,7 @@ sealed interface FakeNetworkEventModel { @DataActivityType val activity: Int?, val carrierNetworkChange: Boolean, val roaming: Boolean, + val name: String, ) : FakeNetworkEventModel data class MobileDisabled( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index a9e50f6b22932..7e9a9cea9b95b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -17,26 +17,32 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod import android.content.Context +import android.content.IntentFilter import android.database.ContentObserver import android.provider.Settings.Global import android.telephony.CellSignalStrength import android.telephony.CellSignalStrengthCdma import android.telephony.ServiceState import android.telephony.SignalStrength +import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.TelephonyCallback import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE import android.telephony.TelephonyManager import android.telephony.TelephonyManager.ERI_OFF +import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN +import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.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.model.toNetworkNameModel 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 @@ -63,8 +69,11 @@ import kotlinx.coroutines.flow.stateIn class MobileConnectionRepositoryImpl( private val context: Context, override val subId: Int, + defaultNetworkName: NetworkNameModel, + networkNameSeparator: String, private val telephonyManager: TelephonyManager, private val globalSettings: GlobalSettings, + broadcastDispatcher: BroadcastDispatcher, defaultDataSubId: StateFlow, globalMobileDataSettingChangedEvent: Flow, mobileMappingsProxy: MobileMappingsProxy, @@ -101,6 +110,7 @@ class MobileConnectionRepositoryImpl( state.copy( isEmergencyOnly = serviceState.isEmergencyOnly, isRoaming = serviceState.roaming, + operatorAlphaShort = serviceState.operatorAlphaShort, ) trySend(state) } @@ -222,6 +232,19 @@ class MobileConnectionRepositoryImpl( .mapLatest { telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF } .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val networkName: StateFlow = + broadcastDispatcher + .broadcastFlow(IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) { + intent, + _ -> + if (intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) != subId) { + defaultNetworkName + } else { + intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName + } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName) + override val dataEnabled: StateFlow = telephonyPollingEvent .mapLatest { dataConnectionAllowed() } @@ -237,6 +260,7 @@ class MobileConnectionRepositoryImpl( class Factory @Inject constructor( + private val broadcastDispatcher: BroadcastDispatcher, private val context: Context, private val telephonyManager: TelephonyManager, private val logger: ConnectivityPipelineLogger, @@ -247,14 +271,19 @@ class MobileConnectionRepositoryImpl( ) { fun build( subId: Int, + defaultNetworkName: NetworkNameModel, + networkNameSeparator: String, defaultDataSubId: StateFlow, globalMobileDataSettingChangedEvent: Flow, ): MobileConnectionRepository { return MobileConnectionRepositoryImpl( context, subId, + defaultNetworkName, + networkNameSeparator, telephonyManager.createForSubscriptionId(subId), globalSettings, + broadcastDispatcher, defaultDataSubId, globalMobileDataSettingChangedEvent, mobileMappingsProxy, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index 483df4759d28f..a9b3d18774fd1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -38,12 +38,14 @@ import androidx.annotation.VisibleForTesting import com.android.internal.telephony.PhoneConstants import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.mobile.MobileMappings.Config +import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 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.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository @@ -88,6 +90,14 @@ constructor( ) : MobileConnectionsRepository { private var subIdRepositoryCache: MutableMap = mutableMapOf() + private val defaultNetworkName = + NetworkNameModel.Default( + context.getString(com.android.internal.R.string.lockscreen_carrier_default) + ) + + private val networkNameSeparator: String = + context.getString(R.string.status_bar_network_name_separator) + /** * 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 @@ -243,6 +253,8 @@ constructor( private fun createRepositoryForSubId(subId: Int): MobileConnectionRepository { return mobileConnectionRepositoryFactory.build( subId, + defaultNetworkName, + networkNameSeparator, defaultDataSubId, globalMobileDataSettingChangedEvent, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index ae39d12ecbde4..76e6a96a19d75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -20,6 +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.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.util.CarrierConfigTracker @@ -56,6 +57,15 @@ interface MobileIconInteractor { /** Observable for RAT type (network type) indicator */ val networkTypeIconGroup: StateFlow + /** + * Provider name for this network connection. The name can be one of 3 values: + * 1. The default network name, if one is configured + * 2. A derived name based off of the intent [ACTION_SERVICE_PROVIDERS_UPDATED] + * 3. Or, in the case where the repository sends us the default network name, we check for an + * override in [connectionInfo.operatorAlphaShort], a value that is derived from [ServiceState] + */ + val networkName: StateFlow + /** True if this line of service is emergency-only */ val isEmergencyOnly: StateFlow @@ -93,6 +103,22 @@ class MobileIconInteractorImpl( override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled + override val networkName = + combine(connectionInfo, connectionRepository.networkName) { connection, networkName -> + if ( + networkName is NetworkNameModel.Default && connection.operatorAlphaShort != null + ) { + NetworkNameModel.Derived(connection.operatorAlphaShort) + } else { + networkName + } + } + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + connectionRepository.networkName.value + ) + /** Observable for the current RAT indicator icon ([MobileIconGroup]) */ override val networkTypeIconGroup: StateFlow = combine( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt index 7b9929d963d3d..59eec5327c12b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import kotlinx.coroutines.flow.MutableStateFlow // TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionRepository @@ -32,6 +33,9 @@ class FakeMobileConnectionRepository(override val subId: Int) : MobileConnection override val cdmaRoaming = MutableStateFlow(false) + override val networkName = + MutableStateFlow(NetworkNameModel.Default("default")) + fun setConnectionInfo(model: MobileConnectionModel) { _connectionInfo.value = model } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt index 9b602cc1b34ea..3d5316d1f19d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt @@ -25,6 +25,7 @@ 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.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel import com.android.systemui.util.mockito.mock @@ -98,6 +99,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC activity = testCase.activity, carrierNetworkChange = testCase.carrierNetworkChange, roaming = testCase.roaming, + name = "demo name", ) fakeNetworkEventFlow.value = networkModel @@ -121,6 +123,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC assertThat(connectionInfo.carrierNetworkChangeActive) .isEqualTo(model.carrierNetworkChange) assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming) + assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name)) // TODO(b/261029387): check these once we start handling them assertThat(connectionInfo.isEmergencyOnly).isFalse() @@ -144,6 +147,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC @Annotation.DataActivityType val activity: Int, val carrierNetworkChange: Boolean, val roaming: Boolean, + val name: String, ) { override fun toString(): String { return "INPUT(level=$level, " + @@ -153,7 +157,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC "inflateStrength=$inflateStrength, " + "activity=$activity, " + "carrierNetworkChange=$carrierNetworkChange, " + - "roaming=$roaming)" + "roaming=$roaming, " + + "name=$name)" } // Convenience for iterating test data and creating new cases @@ -166,6 +171,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC @Annotation.DataActivityType activity: Int? = null, carrierNetworkChange: Boolean? = null, roaming: Boolean? = null, + name: String? = null, ): TestCase = TestCase( level = level ?: this.level, @@ -176,6 +182,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC activity = activity ?: this.activity, carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange, roaming = roaming ?: this.roaming, + name = name ?: this.name, ) } @@ -204,6 +211,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC private val carrierNetworkChange = booleanList // false first so the base case doesn't have roaming set (more common) private val roaming = listOf(false, true) + private val names = listOf("name 1", "name 2") @Parameters(name = "{0}") @JvmStatic fun data() = testData() @@ -239,6 +247,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC activity.first(), carrierNetworkChange.first(), roaming.first(), + names.first(), ) val tail = @@ -249,7 +258,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC inflateStrength.map { baseCase.modifiedBy(inflateStrength = it) }, activity.map { baseCase.modifiedBy(activity = it) }, carrierNetworkChange.map { baseCase.modifiedBy(carrierNetworkChange = it) }, - roaming.map { baseCase.modifiedBy(roaming = it) } + roaming.map { baseCase.modifiedBy(roaming = it) }, + names.map { baseCase.modifiedBy(name = it) }, ) .flatten() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt index 8714a9e0471bf..34f30eb7c0a69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt @@ -25,6 +25,7 @@ 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.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled @@ -296,6 +297,7 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(connectionInfo.carrierNetworkChangeActive) .isEqualTo(model.carrierNetworkChange) assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming) + assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name)) // TODO(b/261029387) check these once we start handling them assertThat(connectionInfo.isEmergencyOnly).isFalse() @@ -328,4 +330,5 @@ fun validMobileEvent( activity = activity, carrierNetworkChange = carrierNetworkChange, roaming = roaming, + name = "demo name", ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt index ebfe4f03d173f..7fa80653f29c9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod +import android.content.Intent import android.os.UserHandle import android.provider.Settings import android.telephony.CellSignalStrengthCdma @@ -40,15 +41,22 @@ import android.telephony.TelephonyManager.DATA_DISCONNECTING import android.telephony.TelephonyManager.DATA_UNKNOWN import android.telephony.TelephonyManager.ERI_OFF import android.telephony.TelephonyManager.ERI_ON +import android.telephony.TelephonyManager.EXTRA_PLMN +import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN +import android.telephony.TelephonyManager.EXTRA_SHOW_SPN +import android.telephony.TelephonyManager.EXTRA_SPN +import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID import android.telephony.TelephonyManager.NETWORK_TYPE_LTE import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.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.toNetworkNameModel 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 @@ -98,8 +106,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { MobileConnectionRepositoryImpl( context, SUB_1_ID, + DEFAULT_NAME, + SEP, telephonyManager, globalSettings, + fakeBroadcastDispatcher, connectionsRepo.defaultDataSubId, connectionsRepo.globalMobileDataSettingChangedEvent, mobileMappings, @@ -506,6 +517,74 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { job.cancel() } + @Test + fun `network name - default`() = + runBlocking(IMMEDIATE) { + var latest: NetworkNameModel? = null + val job = underTest.networkName.onEach { latest = it }.launchIn(this) + + assertThat(latest).isEqualTo(DEFAULT_NAME) + + job.cancel() + } + + @Test + fun `network name - uses broadcast info - returns derived`() = + runBlocking(IMMEDIATE) { + var latest: NetworkNameModel? = null + val job = underTest.networkName.onEach { latest = it }.launchIn(this) + + val intent = spnIntent() + + fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> + receiver.onReceive(context, intent) + } + + assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP)) + + job.cancel() + } + + @Test + fun `network name - broadcast not for this sub id - returns default`() = + runBlocking(IMMEDIATE) { + var latest: NetworkNameModel? = null + val job = underTest.networkName.onEach { latest = it }.launchIn(this) + + val intent = spnIntent(subId = 101) + + fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> + receiver.onReceive(context, intent) + } + + assertThat(latest).isEqualTo(DEFAULT_NAME) + + job.cancel() + } + + @Test + fun `network name - operatorAlphaShort - tracked`() = + runBlocking(IMMEDIATE) { + var latest: String? = null + + val job = + underTest.connectionInfo.onEach { latest = it.operatorAlphaShort }.launchIn(this) + + val shortName = "short name" + val serviceState = ServiceState() + serviceState.setOperatorName( + /* longName */ "long name", + /* shortName */ shortName, + /* numeric */ "12345", + ) + + getTelephonyCallbackForType().onServiceStateChanged(serviceState) + + assertThat(latest).isEqualTo(shortName) + + job.cancel() + } + private fun getTelephonyCallbacks(): List { val callbackCaptor = argumentCaptor() Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture()) @@ -531,10 +610,31 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { return signalStrength } + private fun spnIntent( + subId: Int = SUB_1_ID, + showSpn: Boolean = true, + spn: String = SPN, + showPlmn: Boolean = true, + plmn: String = PLMN, + ): Intent = + Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply { + putExtra(EXTRA_SUBSCRIPTION_ID, subId) + putExtra(EXTRA_SHOW_SPN, showSpn) + putExtra(EXTRA_SPN, spn) + putExtra(EXTRA_SHOW_PLMN, showPlmn) + putExtra(EXTRA_PLMN, plmn) + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate private const val SUB_1_ID = 1 private val SUB_1 = mock().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) } + + private val DEFAULT_NAME = NetworkNameModel.Default("default name") + private const val SEP = "-" + + private const val SPN = "testSpn" + private const val PLMN = "testPlmn" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 6d80acbb65ac0..3cc1e8b74668e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -91,6 +91,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { connectionFactory = MobileConnectionRepositoryImpl.Factory( + fakeBroadcastDispatcher, context = context, telephonyManager = telephonyManager, bgDispatcher = IMMEDIATE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index 27a0b55155107..c3519b7c81767 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor import android.telephony.CellSignalStrength import com.android.settingslib.SignalIcon import com.android.settingslib.mobile.TelephonyIcons +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import kotlinx.coroutines.flow.MutableStateFlow @@ -36,6 +37,8 @@ class FakeMobileIconInteractor : MobileIconInteractor { private val _iconGroup = MutableStateFlow(TelephonyIcons.THREE_G) override val networkTypeIconGroup = _iconGroup + override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo mode")) + private val _isEmergencyOnly = MutableStateFlow(false) override val isEmergencyOnly = _isEmergencyOnly diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index 9b6f6df90174b..4dca780425e54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -25,6 +25,7 @@ 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.MobileConnectionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel 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 @@ -392,6 +393,41 @@ class MobileIconInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun `network name - uses operatorAlphaShot when non null and repo is default`() = + runBlocking(IMMEDIATE) { + var latest: NetworkNameModel? = null + val job = underTest.networkName.onEach { latest = it }.launchIn(this) + + val testOperatorName = "operatorAlphaShort" + + // Default network name, operator name is non-null, uses the operator name + connectionRepository.networkName.value = DEFAULT_NAME + connectionRepository.setConnectionInfo( + MobileConnectionModel(operatorAlphaShort = testOperatorName) + ) + yield() + + assertThat(latest).isEqualTo(NetworkNameModel.Derived(testOperatorName)) + + // Default network name, operator name is null, uses the default + connectionRepository.setConnectionInfo(MobileConnectionModel(operatorAlphaShort = null)) + yield() + + assertThat(latest).isEqualTo(DEFAULT_NAME) + + // Derived network name, operator name non-null, uses the derived name + connectionRepository.networkName.value = DERIVED_NAME + connectionRepository.setConnectionInfo( + MobileConnectionModel(operatorAlphaShort = testOperatorName) + ) + yield() + + assertThat(latest).isEqualTo(DERIVED_NAME) + + job.cancel() + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate @@ -401,5 +437,8 @@ class MobileIconInteractorTest : SysuiTestCase() { private const val SUB_1_ID = 1 private val SUB_1 = mock().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) } + + private val DEFAULT_NAME = NetworkNameModel.Default("test default name") + private val DERIVED_NAME = NetworkNameModel.Derived("test derived name") } }