summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-12-28 17:31:07 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-12-28 17:31:07 +0000
commitb425997d1ffda0dda270c1c46e0a85bbf31d5372 (patch)
treef065e07ffc4cca4f6cb64aa0822486875a2c1540
parent67546c8fe14496ec35356a0217a8f9120b33bf14 (diff)
parent937ab161491c6a76c43003b814adc4ccd7e2dc96 (diff)
Merge changes Ie0da0143,I9541a914,I21f3259e,I40a6d5aa,I9c6b9ba3 into tm-qpr-dev am: 933c12efa1 am: 937ab16149
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20803315 Change-Id: Ia39308a0a690799a94be581317dbc3234bdbaced Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--packages/SystemUI/ktfmt_includes.txt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt289
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt311
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt)6
16 files changed, 455 insertions, 302 deletions
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 7290e7ed5394..7243ca4cece0 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -460,7 +460,7 @@
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -752,7 +752,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.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 1aa954ff48cf..012b9ec09e7a 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
@@ -51,6 +51,16 @@ data class MobileConnectionModel(
*/
val operatorAlphaShort: String? = null,
+ /**
+ * TODO (b/263167683): Clarify this field
+ *
+ * This check comes from [com.android.settingslib.Utils.isInService]. It is intended to be a
+ * mapping from a ServiceState to a notion of connectivity. Notably, it will consider a
+ * connection to be in-service if either the voice registration state is IN_SERVICE or the data
+ * registration state is IN_SERVICE and NOT IWLAN.
+ */
+ val isInService: Boolean = false,
+
/** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
val isGsm: Boolean = false,
@IntRange(from = 0, to = 4)
@@ -99,6 +109,10 @@ data class MobileConnectionModel(
row.logChange(COL_OPERATOR, operatorAlphaShort)
}
+ if (prevVal.isInService != isInService) {
+ row.logChange(COL_IS_IN_SERVICE, isInService)
+ }
+
if (prevVal.isGsm != isGsm) {
row.logChange(COL_IS_GSM, isGsm)
}
@@ -129,6 +143,7 @@ data class MobileConnectionModel(
row.logChange(COL_EMERGENCY, isEmergencyOnly)
row.logChange(COL_ROAMING, isRoaming)
row.logChange(COL_OPERATOR, operatorAlphaShort)
+ row.logChange(COL_IS_IN_SERVICE, isInService)
row.logChange(COL_IS_GSM, isGsm)
row.logChange(COL_CDMA_LEVEL, cdmaLevel)
row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
@@ -141,6 +156,7 @@ data class MobileConnectionModel(
const val COL_EMERGENCY = "EmergencyOnly"
const val COL_ROAMING = "Roaming"
const val COL_OPERATOR = "OperatorName"
+ const val COL_IS_IN_SERVICE = "IsInService"
const val COL_IS_GSM = "IsGsm"
const val COL_CDMA_LEVEL = "CdmaLevel"
const val COL_PRIMARY_LEVEL = "PrimaryLevel"
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 b252de8dd389..0b5f9d5ae59e 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
@@ -247,6 +247,7 @@ constructor(
return MobileConnectionModel(
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
isRoaming = roaming,
+ isInService = (level ?: 0) > 0,
isGsm = false, // TODO(b/261029387): not yet supported
cdmaLevel = level ?: 0,
primaryLevel = level ?: 0,
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 0b9e1583898e..5cfff82253c5 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
@@ -32,6 +32,7 @@ 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.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
@@ -117,6 +118,7 @@ class MobileConnectionRepositoryImpl(
isEmergencyOnly = serviceState.isEmergencyOnly,
isRoaming = serviceState.roaming,
operatorAlphaShort = serviceState.operatorAlphaShort,
+ isInService = Utils.isInService(serviceState),
)
trySend(state)
}
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 e6686dce7bbc..675760533d97 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
@@ -48,6 +48,9 @@ interface MobileIconInteractor {
/** True when telephony tells us that the data state is CONNECTED */
val isDataConnected: StateFlow<Boolean>
+ /** True if we consider this connection to be in service, i.e. can make calls */
+ val isInService: StateFlow<Boolean>
+
// TODO(b/256839546): clarify naming of default vs active
/** True if we want to consider the data connection enabled */
val isDefaultDataEnabled: StateFlow<Boolean>
@@ -175,4 +178,9 @@ class MobileIconInteractorImpl(
connectionInfo
.mapLatest { connection -> connection.dataConnectionState == Connected }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ override val isInService =
+ connectionRepository.connectionInfo
+ .mapLatest { it.isInService }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 2d6ac4efd512..a2117c7df188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -80,11 +80,17 @@ constructor(
override val iconId: Flow<Int> = run {
val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
- combine(iconInteractor.level, iconInteractor.numberOfLevels, showExclamationMark) {
- level,
- numberOfLevels,
- showExclamationMark ->
- SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+ combine(
+ iconInteractor.level,
+ iconInteractor.numberOfLevels,
+ showExclamationMark,
+ iconInteractor.isInService,
+ ) { level, numberOfLevels, showExclamationMark, isInService ->
+ if (!isInService) {
+ SignalDrawable.getEmptyState(numberOfLevels)
+ } else {
+ SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+ }
}
.distinctUntilChanged()
.logDiffsForTable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 5ccd6f45770d..53525f254e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -16,51 +16,9 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
-import android.annotation.SuppressLint
-import android.content.IntentFilter
-import android.net.ConnectivityManager
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkRequest
-import android.net.wifi.WifiInfo
-import android.net.wifi.WifiManager
-import android.net.wifi.WifiManager.TrafficStateCallback
-import android.util.Log
-import com.android.settingslib.Utils
-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.Main
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
/** Provides data related to the wifi state. */
interface WifiRepository {
@@ -76,250 +34,3 @@ interface WifiRepository {
/** Observable for the current wifi network activity. */
val wifiActivity: StateFlow<DataActivityModel>
}
-
-/** Real implementation of [WifiRepository]. */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-@SuppressLint("MissingPermission")
-class WifiRepositoryImpl @Inject constructor(
- broadcastDispatcher: BroadcastDispatcher,
- connectivityManager: ConnectivityManager,
- logger: ConnectivityPipelineLogger,
- @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
- @Main mainExecutor: Executor,
- @Application scope: CoroutineScope,
- wifiManager: WifiManager?,
-) : WifiRepository {
-
- private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
- IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
- )
- .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
-
- private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
- MutableSharedFlow(extraBufferCapacity = 1)
-
- override val isWifiEnabled: StateFlow<Boolean> =
- if (wifiManager == null) {
- MutableStateFlow(false).asStateFlow()
- } else {
- // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
- // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
- // have changed.
- merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
- .mapLatest { wifiManager.isWifiEnabled }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "",
- columnName = "isWifiEnabled",
- initialValue = wifiManager.isWifiEnabled,
- )
- .stateIn(
- scope = scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = wifiManager.isWifiEnabled
- )
- }
-
- override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
- // Note: This callback doesn't do any logging because we already log every network change
- // in the [wifiNetwork] callback.
- val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
- override fun onCapabilitiesChanged(
- network: Network,
- networkCapabilities: NetworkCapabilities
- ) {
- // This method will always be called immediately after the network becomes the
- // default, in addition to any time the capabilities change while the network is
- // the default.
- // If this network contains valid wifi info, then wifi is the default network.
- val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
- trySend(wifiInfo != null)
- }
-
- override fun onLost(network: Network) {
- // The system no longer has a default network, so wifi is definitely not default.
- trySend(false)
- }
- }
-
- connectivityManager.registerDefaultNetworkCallback(callback)
- awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "",
- columnName = "isWifiDefault",
- initialValue = false,
- )
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false
- )
-
- override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
- var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
-
- val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
- override fun onCapabilitiesChanged(
- network: Network,
- networkCapabilities: NetworkCapabilities
- ) {
- logger.logOnCapabilitiesChanged(network, networkCapabilities)
-
- wifiNetworkChangeEvents.tryEmit(Unit)
-
- val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
- if (wifiInfo?.isPrimary == true) {
- val wifiNetworkModel = createWifiNetworkModel(
- wifiInfo,
- network,
- networkCapabilities,
- wifiManager,
- )
- logger.logTransformation(
- WIFI_NETWORK_CALLBACK_NAME,
- oldValue = currentWifi,
- newValue = wifiNetworkModel
- )
- currentWifi = wifiNetworkModel
- trySend(wifiNetworkModel)
- }
- }
-
- override fun onLost(network: Network) {
- logger.logOnLost(network)
-
- wifiNetworkChangeEvents.tryEmit(Unit)
-
- val wifi = currentWifi
- if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
- val newNetworkModel = WifiNetworkModel.Inactive
- logger.logTransformation(
- WIFI_NETWORK_CALLBACK_NAME,
- oldValue = wifi,
- newValue = newNetworkModel
- )
- currentWifi = newNetworkModel
- trySend(newNetworkModel)
- }
- }
- }
-
- connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
-
- awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "wifiNetwork",
- initialValue = WIFI_NETWORK_DEFAULT,
- )
- // There will be multiple wifi icons in different places that will frequently
- // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
- // new subscribes will get the latest value immediately upon subscription. Otherwise, the
- // views could show stale data. See b/244173280.
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = WIFI_NETWORK_DEFAULT
- )
-
- override val wifiActivity: StateFlow<DataActivityModel> =
- if (wifiManager == null) {
- Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
- flowOf(ACTIVITY_DEFAULT)
- } else {
- conflatedCallbackFlow {
- val callback = TrafficStateCallback { state ->
- logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
- trySend(state.toWifiDataActivityModel())
- }
- wifiManager.registerTrafficStateCallback(mainExecutor, callback)
- awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
- }
- }
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = ACTIVITY_PREFIX,
- initialValue = ACTIVITY_DEFAULT,
- )
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = ACTIVITY_DEFAULT
- )
-
- companion object {
- private const val ACTIVITY_PREFIX = "wifiActivity"
-
- val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- // Start out with no known wifi network.
- // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
- // initial fetch to get a starting wifi network. But, it uses a deprecated API
- // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
- // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
- // NetworkCallback inside [wifiNetwork] for our wifi network information.
- val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
-
- private fun networkCapabilitiesToWifiInfo(
- networkCapabilities: NetworkCapabilities
- ): WifiInfo? {
- return when {
- networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
- networkCapabilities.transportInfo as WifiInfo?
- networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
- // Sometimes, cellular networks can act as wifi networks (known as VCN --
- // virtual carrier network). So, see if this cellular network has wifi info.
- Utils.tryGetWifiInfoForVcn(networkCapabilities)
- else -> null
- }
- }
-
- private fun createWifiNetworkModel(
- wifiInfo: WifiInfo,
- network: Network,
- networkCapabilities: NetworkCapabilities,
- wifiManager: WifiManager?,
- ): WifiNetworkModel {
- return if (wifiInfo.isCarrierMerged) {
- WifiNetworkModel.CarrierMerged
- } else {
- WifiNetworkModel.Active(
- network.getNetId(),
- isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
- level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
- wifiInfo.ssid,
- wifiInfo.isPasspointAp,
- wifiInfo.isOsuAp,
- wifiInfo.passpointProviderFriendlyName
- )
- }
- }
-
- private fun prettyPrintActivity(activity: Int): String {
- return when (activity) {
- TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
- TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
- TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
- TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
- else -> "INVALID"
- }
- }
-
- private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
- NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NET_CAPABILITY_NOT_VPN)
- .addTransportType(TRANSPORT_WIFI)
- .addTransportType(TRANSPORT_CELLULAR)
- .build()
-
- private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 73bcdfd2b78e..be86620e01f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -25,6 +25,7 @@ import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
new file mode 100644
index 000000000000..c8c94e102999
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -0,0 +1,311 @@
+/*
+ * 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.wifi.data.repository.prod
+
+import android.annotation.SuppressLint
+import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.wifi.WifiInfo
+import android.net.wifi.WifiManager
+import android.net.wifi.WifiManager.TrafficStateCallback
+import android.util.Log
+import com.android.settingslib.Utils
+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.Main
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
+
+/** Real implementation of [WifiRepository]. */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+@SuppressLint("MissingPermission")
+class WifiRepositoryImpl @Inject constructor(
+ broadcastDispatcher: BroadcastDispatcher,
+ connectivityManager: ConnectivityManager,
+ logger: ConnectivityPipelineLogger,
+ @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
+ @Main mainExecutor: Executor,
+ @Application scope: CoroutineScope,
+ wifiManager: WifiManager?,
+) : WifiRepository {
+
+ private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
+ IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
+ )
+ .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
+
+ private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
+ MutableSharedFlow(extraBufferCapacity = 1)
+
+ override val isWifiEnabled: StateFlow<Boolean> =
+ if (wifiManager == null) {
+ MutableStateFlow(false).asStateFlow()
+ } else {
+ // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
+ // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
+ // have changed.
+ merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
+ .mapLatest { wifiManager.isWifiEnabled }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiEnabled",
+ initialValue = wifiManager.isWifiEnabled,
+ )
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = wifiManager.isWifiEnabled
+ )
+ }
+
+ override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
+ // Note: This callback doesn't do any logging because we already log every network change
+ // in the [wifiNetwork] callback.
+ val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities
+ ) {
+ // This method will always be called immediately after the network becomes the
+ // default, in addition to any time the capabilities change while the network is
+ // the default.
+ // If this network contains valid wifi info, then wifi is the default network.
+ val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+ trySend(wifiInfo != null)
+ }
+
+ override fun onLost(network: Network) {
+ // The system no longer has a default network, so wifi is definitely not default.
+ trySend(false)
+ }
+ }
+
+ connectivityManager.registerDefaultNetworkCallback(callback)
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiDefault",
+ initialValue = false,
+ )
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
+
+ override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
+ var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
+
+ val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities
+ ) {
+ logger.logOnCapabilitiesChanged(network, networkCapabilities)
+
+ wifiNetworkChangeEvents.tryEmit(Unit)
+
+ val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+ if (wifiInfo?.isPrimary == true) {
+ val wifiNetworkModel = createWifiNetworkModel(
+ wifiInfo,
+ network,
+ networkCapabilities,
+ wifiManager,
+ )
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = currentWifi,
+ newValue = wifiNetworkModel
+ )
+ currentWifi = wifiNetworkModel
+ trySend(wifiNetworkModel)
+ }
+ }
+
+ override fun onLost(network: Network) {
+ logger.logOnLost(network)
+
+ wifiNetworkChangeEvents.tryEmit(Unit)
+
+ val wifi = currentWifi
+ if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
+ val newNetworkModel = WifiNetworkModel.Inactive
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = wifi,
+ newValue = newNetworkModel
+ )
+ currentWifi = newNetworkModel
+ trySend(newNetworkModel)
+ }
+ }
+ }
+
+ connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
+
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "wifiNetwork",
+ initialValue = WIFI_NETWORK_DEFAULT,
+ )
+ // There will be multiple wifi icons in different places that will frequently
+ // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
+ // new subscribes will get the latest value immediately upon subscription. Otherwise, the
+ // views could show stale data. See b/244173280.
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = WIFI_NETWORK_DEFAULT
+ )
+
+ override val wifiActivity: StateFlow<DataActivityModel> =
+ if (wifiManager == null) {
+ Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
+ flowOf(ACTIVITY_DEFAULT)
+ } else {
+ conflatedCallbackFlow {
+ val callback = TrafficStateCallback { state ->
+ logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
+ trySend(state.toWifiDataActivityModel())
+ }
+ wifiManager.registerTrafficStateCallback(mainExecutor, callback)
+ awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
+ }
+ }
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = ACTIVITY_PREFIX,
+ initialValue = ACTIVITY_DEFAULT,
+ )
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = ACTIVITY_DEFAULT
+ )
+
+ companion object {
+ private const val ACTIVITY_PREFIX = "wifiActivity"
+
+ val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ // Start out with no known wifi network.
+ // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
+ // initial fetch to get a starting wifi network. But, it uses a deprecated API
+ // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
+ // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
+ // NetworkCallback inside [wifiNetwork] for our wifi network information.
+ val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
+
+ private fun networkCapabilitiesToWifiInfo(
+ networkCapabilities: NetworkCapabilities
+ ): WifiInfo? {
+ return when {
+ networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+ networkCapabilities.transportInfo as WifiInfo?
+ networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+ // Sometimes, cellular networks can act as wifi networks (known as VCN --
+ // virtual carrier network). So, see if this cellular network has wifi info.
+ Utils.tryGetWifiInfoForVcn(networkCapabilities)
+ else -> null
+ }
+ }
+
+ private fun createWifiNetworkModel(
+ wifiInfo: WifiInfo,
+ network: Network,
+ networkCapabilities: NetworkCapabilities,
+ wifiManager: WifiManager?,
+ ): WifiNetworkModel {
+ return if (wifiInfo.isCarrierMerged) {
+ WifiNetworkModel.CarrierMerged
+ } else {
+ WifiNetworkModel.Active(
+ network.getNetId(),
+ isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
+ level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
+ wifiInfo.ssid,
+ wifiInfo.isPasspointAp,
+ wifiInfo.isOsuAp,
+ wifiInfo.passpointProviderFriendlyName
+ )
+ }
+ }
+
+ private fun prettyPrintActivity(activity: Int): String {
+ return when (activity) {
+ TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
+ TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
+ TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
+ TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
+ else -> "INVALID"
+ }
+ }
+
+ private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
+ NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build()
+
+ private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
+ }
+}
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 7970443f69b1..c63dd2a2318c 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
@@ -20,7 +20,10 @@ import android.content.Intent
import android.os.UserHandle
import android.provider.Settings
import android.telephony.CellSignalStrengthCdma
+import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState
+import android.telephony.ServiceState.STATE_IN_SERVICE
+import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
import android.telephony.SignalStrength
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyCallback
@@ -47,7 +50,6 @@ 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.log.table.TableLogBuffer
@@ -302,7 +304,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
- val type = NETWORK_TYPE_UNKNOWN
val expected = UnknownNetworkType
assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
@@ -590,6 +591,56 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `connection model - isInService - not iwlan`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+ val serviceState = ServiceState()
+ serviceState.voiceRegState = STATE_IN_SERVICE
+ serviceState.dataRegState = STATE_IN_SERVICE
+
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+
+ assertThat(latest).isTrue()
+
+ serviceState.voiceRegState = STATE_OUT_OF_SERVICE
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isTrue()
+
+ serviceState.dataRegState = STATE_OUT_OF_SERVICE
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `connection model - isInService - is iwlan - voice out of service - data in service`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+ // Mock the service state here so we can make it specifically IWLAN
+ val serviceState: ServiceState = mock()
+ whenever(serviceState.state).thenReturn(STATE_OUT_OF_SERVICE)
+ whenever(serviceState.dataRegistrationState).thenReturn(STATE_IN_SERVICE)
+
+ // See [com.android.settingslib.Utils.isInService] for more info. This is one way to
+ // make the network look like IWLAN
+ val networkRegWlan: NetworkRegistrationInfo = mock()
+ whenever(serviceState.getNetworkRegistrationInfo(any(), any()))
+ .thenReturn(networkRegWlan)
+ whenever(networkRegWlan.registrationState)
+ .thenReturn(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
private fun getTelephonyCallbacks(): List<TelephonyCallback> {
val callbackCaptor = argumentCaptor<TelephonyCallback>()
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
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 c49458909c78..5889ec885e1f 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
@@ -52,6 +52,8 @@ class FakeMobileIconInteractor(
override val isDataConnected = MutableStateFlow(true)
+ override val isInService = MutableStateFlow(true)
+
private val _isDataEnabled = MutableStateFlow(true)
override val isDataEnabled = _isDataEnabled
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 83c5055a6eda..cb2ee9940c38 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
@@ -300,6 +300,23 @@ class MobileIconInteractorTest : SysuiTestCase() {
}
@Test
+ fun `isInService - uses repository value`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isInService.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = true))
+
+ assertThat(latest).isTrue()
+
+ connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = false))
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun `roaming - is gsm - uses connection model`() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 50221bc97bad..2a8d42ff6997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -98,6 +98,30 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
+ fun `icon - uses empty state - when not in service`() =
+ testScope.runTest {
+ var latest: Int? = null
+ val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+
+ interactor.isInService.value = false
+
+ var expected = emptySignal()
+
+ assertThat(latest).isEqualTo(expected)
+
+ // Changing the level doesn't overwrite the disabled state
+ interactor.level.value = 2
+ assertThat(latest).isEqualTo(expected)
+
+ // Once back in service, the regular icon appears
+ interactor.isInService.value = true
+ expected = defaultSignal(level = 2)
+ assertThat(latest).isEqualTo(expected)
+
+ job.cancel()
+ }
+
+ @Test
fun networkType_dataEnabled_groupIsRepresented() =
testScope.runTest {
val expected =
@@ -375,5 +399,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
): Int {
return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
}
+
+ fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 4e15b4a503d7..f5837d698c51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index b935442fd73a..1085c2b1a5d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 5d0d87b13d20..befb2901d4d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.wifi.data.repository
+package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
import android.net.ConnectivityManager
import android.net.Network
@@ -33,8 +33,8 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor