Merge changes Ie0da0143,I9541a914,I21f3259e,I40a6d5aa,I9c6b9ba3 into tm-qpr-dev am: 933c12efa1
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20803315 Change-Id: I0b0476828bdfd8b4a5d2d478dfba0f8f826e0123 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -460,7 +460,7 @@
|
|||||||
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
|
-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/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/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/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/shared/WifiConstants.kt
|
||||||
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.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/panelstate/ShadeExpansionStateManagerTest.kt
|
||||||
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.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/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/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/view/ModernStatusBarWifiViewTest.kt
|
||||||
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
|
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
|
||||||
|
|||||||
@@ -51,6 +51,16 @@ data class MobileConnectionModel(
|
|||||||
*/
|
*/
|
||||||
val operatorAlphaShort: String? = null,
|
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] */
|
/** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
|
||||||
val isGsm: Boolean = false,
|
val isGsm: Boolean = false,
|
||||||
@IntRange(from = 0, to = 4)
|
@IntRange(from = 0, to = 4)
|
||||||
@@ -99,6 +109,10 @@ data class MobileConnectionModel(
|
|||||||
row.logChange(COL_OPERATOR, operatorAlphaShort)
|
row.logChange(COL_OPERATOR, operatorAlphaShort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevVal.isInService != isInService) {
|
||||||
|
row.logChange(COL_IS_IN_SERVICE, isInService)
|
||||||
|
}
|
||||||
|
|
||||||
if (prevVal.isGsm != isGsm) {
|
if (prevVal.isGsm != isGsm) {
|
||||||
row.logChange(COL_IS_GSM, isGsm)
|
row.logChange(COL_IS_GSM, isGsm)
|
||||||
}
|
}
|
||||||
@@ -129,6 +143,7 @@ data class MobileConnectionModel(
|
|||||||
row.logChange(COL_EMERGENCY, isEmergencyOnly)
|
row.logChange(COL_EMERGENCY, isEmergencyOnly)
|
||||||
row.logChange(COL_ROAMING, isRoaming)
|
row.logChange(COL_ROAMING, isRoaming)
|
||||||
row.logChange(COL_OPERATOR, operatorAlphaShort)
|
row.logChange(COL_OPERATOR, operatorAlphaShort)
|
||||||
|
row.logChange(COL_IS_IN_SERVICE, isInService)
|
||||||
row.logChange(COL_IS_GSM, isGsm)
|
row.logChange(COL_IS_GSM, isGsm)
|
||||||
row.logChange(COL_CDMA_LEVEL, cdmaLevel)
|
row.logChange(COL_CDMA_LEVEL, cdmaLevel)
|
||||||
row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
|
row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
|
||||||
@@ -141,6 +156,7 @@ data class MobileConnectionModel(
|
|||||||
const val COL_EMERGENCY = "EmergencyOnly"
|
const val COL_EMERGENCY = "EmergencyOnly"
|
||||||
const val COL_ROAMING = "Roaming"
|
const val COL_ROAMING = "Roaming"
|
||||||
const val COL_OPERATOR = "OperatorName"
|
const val COL_OPERATOR = "OperatorName"
|
||||||
|
const val COL_IS_IN_SERVICE = "IsInService"
|
||||||
const val COL_IS_GSM = "IsGsm"
|
const val COL_IS_GSM = "IsGsm"
|
||||||
const val COL_CDMA_LEVEL = "CdmaLevel"
|
const val COL_CDMA_LEVEL = "CdmaLevel"
|
||||||
const val COL_PRIMARY_LEVEL = "PrimaryLevel"
|
const val COL_PRIMARY_LEVEL = "PrimaryLevel"
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ constructor(
|
|||||||
return MobileConnectionModel(
|
return MobileConnectionModel(
|
||||||
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
|
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
|
||||||
isRoaming = roaming,
|
isRoaming = roaming,
|
||||||
|
isInService = (level ?: 0) > 0,
|
||||||
isGsm = false, // TODO(b/261029387): not yet supported
|
isGsm = false, // TODO(b/261029387): not yet supported
|
||||||
cdmaLevel = level ?: 0,
|
cdmaLevel = level ?: 0,
|
||||||
primaryLevel = level ?: 0,
|
primaryLevel = level ?: 0,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import android.telephony.TelephonyManager
|
|||||||
import android.telephony.TelephonyManager.ERI_OFF
|
import android.telephony.TelephonyManager.ERI_OFF
|
||||||
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
|
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
|
||||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
||||||
|
import com.android.settingslib.Utils
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
@@ -117,6 +118,7 @@ class MobileConnectionRepositoryImpl(
|
|||||||
isEmergencyOnly = serviceState.isEmergencyOnly,
|
isEmergencyOnly = serviceState.isEmergencyOnly,
|
||||||
isRoaming = serviceState.roaming,
|
isRoaming = serviceState.roaming,
|
||||||
operatorAlphaShort = serviceState.operatorAlphaShort,
|
operatorAlphaShort = serviceState.operatorAlphaShort,
|
||||||
|
isInService = Utils.isInService(serviceState),
|
||||||
)
|
)
|
||||||
trySend(state)
|
trySend(state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ interface MobileIconInteractor {
|
|||||||
/** True when telephony tells us that the data state is CONNECTED */
|
/** True when telephony tells us that the data state is CONNECTED */
|
||||||
val isDataConnected: StateFlow<Boolean>
|
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
|
// TODO(b/256839546): clarify naming of default vs active
|
||||||
/** True if we want to consider the data connection enabled */
|
/** True if we want to consider the data connection enabled */
|
||||||
val isDefaultDataEnabled: StateFlow<Boolean>
|
val isDefaultDataEnabled: StateFlow<Boolean>
|
||||||
@@ -175,4 +178,9 @@ class MobileIconInteractorImpl(
|
|||||||
connectionInfo
|
connectionInfo
|
||||||
.mapLatest { connection -> connection.dataConnectionState == Connected }
|
.mapLatest { connection -> connection.dataConnectionState == Connected }
|
||||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||||
|
|
||||||
|
override val isInService =
|
||||||
|
connectionRepository.connectionInfo
|
||||||
|
.mapLatest { it.isInService }
|
||||||
|
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,11 +80,17 @@ constructor(
|
|||||||
|
|
||||||
override val iconId: Flow<Int> = run {
|
override val iconId: Flow<Int> = run {
|
||||||
val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
|
val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
|
||||||
combine(iconInteractor.level, iconInteractor.numberOfLevels, showExclamationMark) {
|
combine(
|
||||||
level,
|
iconInteractor.level,
|
||||||
numberOfLevels,
|
iconInteractor.numberOfLevels,
|
||||||
showExclamationMark ->
|
showExclamationMark,
|
||||||
SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
|
iconInteractor.isInService,
|
||||||
|
) { level, numberOfLevels, showExclamationMark, isInService ->
|
||||||
|
if (!isInService) {
|
||||||
|
SignalDrawable.getEmptyState(numberOfLevels)
|
||||||
|
} else {
|
||||||
|
SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.logDiffsForTable(
|
.logDiffsForTable(
|
||||||
|
|||||||
@@ -16,51 +16,9 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
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.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.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.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. */
|
/** Provides data related to the wifi state. */
|
||||||
interface WifiRepository {
|
interface WifiRepository {
|
||||||
@@ -76,250 +34,3 @@ interface WifiRepository {
|
|||||||
/** Observable for the current wifi network activity. */
|
/** Observable for the current wifi network activity. */
|
||||||
val wifiActivity: StateFlow<DataActivityModel>
|
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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.shared.data.model.DataActivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
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.demo.DemoWifiRepository
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,10 @@ import android.content.Intent
|
|||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.telephony.CellSignalStrengthCdma
|
import android.telephony.CellSignalStrengthCdma
|
||||||
|
import android.telephony.NetworkRegistrationInfo
|
||||||
import android.telephony.ServiceState
|
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.SignalStrength
|
||||||
import android.telephony.SubscriptionInfo
|
import android.telephony.SubscriptionInfo
|
||||||
import android.telephony.TelephonyCallback
|
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_SPN
|
||||||
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
|
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
|
||||||
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
|
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
|
||||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.log.table.TableLogBuffer
|
import com.android.systemui.log.table.TableLogBuffer
|
||||||
@@ -302,7 +304,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
|||||||
var latest: MobileConnectionModel? = null
|
var latest: MobileConnectionModel? = null
|
||||||
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
val type = NETWORK_TYPE_UNKNOWN
|
|
||||||
val expected = UnknownNetworkType
|
val expected = UnknownNetworkType
|
||||||
|
|
||||||
assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
|
assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
|
||||||
@@ -590,6 +591,56 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
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> {
|
private fun getTelephonyCallbacks(): List<TelephonyCallback> {
|
||||||
val callbackCaptor = argumentCaptor<TelephonyCallback>()
|
val callbackCaptor = argumentCaptor<TelephonyCallback>()
|
||||||
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
|
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class FakeMobileIconInteractor(
|
|||||||
|
|
||||||
override val isDataConnected = MutableStateFlow(true)
|
override val isDataConnected = MutableStateFlow(true)
|
||||||
|
|
||||||
|
override val isInService = MutableStateFlow(true)
|
||||||
|
|
||||||
private val _isDataEnabled = MutableStateFlow(true)
|
private val _isDataEnabled = MutableStateFlow(true)
|
||||||
override val isDataEnabled = _isDataEnabled
|
override val isDataEnabled = _isDataEnabled
|
||||||
|
|
||||||
|
|||||||
@@ -299,6 +299,23 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
fun `roaming - is gsm - uses connection model`() =
|
fun `roaming - is gsm - uses connection model`() =
|
||||||
runBlocking(IMMEDIATE) {
|
runBlocking(IMMEDIATE) {
|
||||||
|
|||||||
@@ -97,6 +97,30 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
fun networkType_dataEnabled_groupIsRepresented() =
|
fun networkType_dataEnabled_groupIsRepresented() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
@@ -375,5 +399,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
|||||||
): Int {
|
): Int {
|
||||||
return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
|
return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.shared.data.model.DataActivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
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.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
|||||||
@@ -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.DemoModeWifiDataSource
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
|
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.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.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* 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.ConnectivityManager
|
||||||
import android.net.Network
|
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.ConnectivityPipelineLogger
|
||||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
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.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 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.WIFI_NETWORK_DEFAULT
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
Reference in New Issue
Block a user