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") } }