Merge "[SB Refactor] Implement the wifi network flow using the NetworkCallback." into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e805c9bf06
@@ -600,6 +600,9 @@ public class Utils {
|
||||
* Returns the WifiInfo for the underlying WiFi network of the VCN network, returns null if the
|
||||
* input NetworkCapabilities is not for a VCN network with underlying WiFi network.
|
||||
*
|
||||
* TODO(b/238425913): Move this method to be inside systemui not settingslib once we've migrated
|
||||
* off of {@link WifiStatusTracker} and {@link NetworkControllerImpl}.
|
||||
*
|
||||
* @param networkCapabilities NetworkCapabilities of the network.
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Interface exposing a flow for raw connectivity information. Clients should collect on
|
||||
* [rawConnectivityInfoFlow] to get updates on connectivity information.
|
||||
*
|
||||
* Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
|
||||
* and all clients get references to the same flow.
|
||||
*
|
||||
* This will be used for the new status bar pipeline to compile information we need to display some
|
||||
* of the icons in the RHS of the status bar.
|
||||
*/
|
||||
interface ConnectivityInfoCollector {
|
||||
val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing all of the raw connectivity information.
|
||||
*
|
||||
* Importantly, all the information in this object should not be processed at all (i.e., the data
|
||||
* that we receive from callbacks should be piped straight into this object and not be filtered,
|
||||
* manipulated, or processed in any way). Instead, any listeners on
|
||||
* [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
|
||||
*
|
||||
* This allows us to keep all the processing in one place which is beneficial for logging and
|
||||
* debugging purposes.
|
||||
*/
|
||||
data class RawConnectivityInfo(
|
||||
val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
|
||||
)
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilitiesRepo
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* The real implementation of [ConnectivityInfoCollector] that will collect information from all the
|
||||
* relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
|
||||
*/
|
||||
@SysUISingleton
|
||||
class ConnectivityInfoCollectorImpl @Inject constructor(
|
||||
networkCapabilitiesRepo: NetworkCapabilitiesRepo,
|
||||
@Application scope: CoroutineScope,
|
||||
) : ConnectivityInfoCollector {
|
||||
override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
|
||||
// TODO(b/238425913): Collect all the separate flows for individual raw information into
|
||||
// this final flow.
|
||||
networkCapabilitiesRepo.dataStream
|
||||
.map {
|
||||
RawConnectivityInfo(networkCapabilityInfo = it)
|
||||
}
|
||||
.stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
|
||||
}
|
||||
@@ -20,31 +20,21 @@ import android.content.Context
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* A processor that transforms raw connectivity information that we get from callbacks and turns it
|
||||
* into a list of displayable connectivity information.
|
||||
* A temporary object that collects on [WifiViewModel] flows for debugging purposes.
|
||||
*
|
||||
* This will be used for the new status bar pipeline to calculate the list of icons that should be
|
||||
* displayed in the RHS of the status bar.
|
||||
*
|
||||
* Anyone can listen to [processedInfoFlow] to get updates to the processed data.
|
||||
* This will eventually get migrated to a view binder that will use the flow outputs to set state on
|
||||
* views. For now, this just collects on flows so that the information gets logged.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class ConnectivityInfoProcessor @Inject constructor(
|
||||
connectivityInfoCollector: ConnectivityInfoCollector,
|
||||
context: Context,
|
||||
// TODO(b/238425913): Don't use the application scope; instead, use the status bar view's
|
||||
// scope so we only do work when there's UI that cares about it.
|
||||
@@ -52,21 +42,6 @@ class ConnectivityInfoProcessor @Inject constructor(
|
||||
private val statusBarPipelineFlags: StatusBarPipelineFlags,
|
||||
private val wifiViewModelProvider: Provider<WifiViewModel>,
|
||||
) : CoreStartable(context) {
|
||||
// Note: This flow will not start running until a client calls `collect` on it, which means that
|
||||
// [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
|
||||
// happens.
|
||||
// TODO(b/238425913): Delete this.
|
||||
val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
|
||||
if (!statusBarPipelineFlags.isNewPipelineEnabled())
|
||||
emptyFlow()
|
||||
else connectivityInfoCollector.rawConnectivityInfoFlow
|
||||
.map { it.process() }
|
||||
.stateIn(
|
||||
scope,
|
||||
started = Lazily,
|
||||
initialValue = ProcessedConnectivityInfo()
|
||||
)
|
||||
|
||||
override fun start() {
|
||||
if (!statusBarPipelineFlags.isNewPipelineEnabled()) {
|
||||
return
|
||||
@@ -77,17 +52,4 @@ class ConnectivityInfoProcessor @Inject constructor(
|
||||
wifiViewModelProvider.get().isActivityInVisible.collect { }
|
||||
}
|
||||
}
|
||||
|
||||
private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
|
||||
// TODO(b/238425913): Actually process the raw info into meaningful data.
|
||||
return ProcessedConnectivityInfo(this.networkCapabilityInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing connectivity info that has been processed into data that can be directly
|
||||
* used by the status bar (and potentially other SysUI areas) to display icons.
|
||||
*/
|
||||
data class ProcessedConnectivityInfo(
|
||||
val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
|
||||
)
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package com.android.systemui.statusbar.pipeline.dagger
|
||||
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
|
||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
|
||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
|
||||
@@ -35,11 +33,6 @@ abstract class StatusBarPipelineModule {
|
||||
@ClassKey(ConnectivityInfoProcessor::class)
|
||||
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
|
||||
|
||||
@Binds
|
||||
abstract fun provideConnectivityInfoCollector(
|
||||
impl: ConnectivityInfoCollectorImpl
|
||||
): ConnectivityInfoCollector
|
||||
|
||||
@Binds
|
||||
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ import kotlinx.coroutines.flow.onEach
|
||||
class ConnectivityPipelineLogger @Inject constructor(
|
||||
@StatusBarConnectivityLog private val buffer: LogBuffer,
|
||||
) {
|
||||
/**
|
||||
* Logs a change in one of the **raw inputs** to the connectivity pipeline.
|
||||
*/
|
||||
fun logInputChange(callbackName: String, changeInfo: String) {
|
||||
buffer.log(
|
||||
SB_LOGGING_TAG,
|
||||
@@ -45,6 +48,41 @@ class ConnectivityPipelineLogger @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a **data transformation** that we performed within the connectivity pipeline.
|
||||
*/
|
||||
fun logTransformation(transformationName: String, oldValue: Any?, newValue: Any?) {
|
||||
if (oldValue == newValue) {
|
||||
buffer.log(
|
||||
SB_LOGGING_TAG,
|
||||
LogLevel.INFO,
|
||||
{
|
||||
str1 = transformationName
|
||||
str2 = oldValue.toString()
|
||||
},
|
||||
{
|
||||
"Transform: $str1: $str2 (transformation didn't change it)"
|
||||
}
|
||||
)
|
||||
} else {
|
||||
buffer.log(
|
||||
SB_LOGGING_TAG,
|
||||
LogLevel.INFO,
|
||||
{
|
||||
str1 = transformationName
|
||||
str2 = oldValue.toString()
|
||||
str3 = newValue.toString()
|
||||
},
|
||||
{
|
||||
"Transform: $str1: $str2 -> $str3"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a change in one of the **outputs** to the connectivity pipeline.
|
||||
*/
|
||||
fun logOutputChange(outputParamName: String, changeInfo: String) {
|
||||
buffer.log(
|
||||
SB_LOGGING_TAG,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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.model
|
||||
|
||||
/** Provides information about the current wifi state. */
|
||||
data class WifiModel(
|
||||
/** See [android.net.wifi.WifiInfo.ssid]. */
|
||||
val ssid: String? = null,
|
||||
/** See [android.net.wifi.WifiInfo.isPasspointAp]. */
|
||||
val isPasspointAccessPoint: Boolean = false,
|
||||
/** See [android.net.wifi.WifiInfo.isOsuAp]. */
|
||||
val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
|
||||
/** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
|
||||
val passpointProviderFriendlyName: String? = null,
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.model
|
||||
|
||||
/** Provides information about the current wifi network. */
|
||||
sealed class WifiNetworkModel {
|
||||
/** A model representing that we have no active wifi network. */
|
||||
object Inactive : WifiNetworkModel()
|
||||
|
||||
/** Provides information about an active wifi network. */
|
||||
class Active(
|
||||
/**
|
||||
* The [android.net.Network.netId] we received from
|
||||
* [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
|
||||
*
|
||||
* Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
|
||||
*/
|
||||
val networkId: Int,
|
||||
|
||||
/** See [android.net.wifi.WifiInfo.ssid]. */
|
||||
val ssid: String? = null,
|
||||
|
||||
/** See [android.net.wifi.WifiInfo.isPasspointAp]. */
|
||||
val isPasspointAccessPoint: Boolean = false,
|
||||
|
||||
/** See [android.net.wifi.WifiInfo.isOsuAp]. */
|
||||
val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
|
||||
|
||||
/** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
|
||||
val passpointProviderFriendlyName: String? = null,
|
||||
) : WifiNetworkModel()
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* Repository that contains all relevant [NetworkCapabilities] for the current networks.
|
||||
*
|
||||
* TODO(b/238425913): Figure out how to merge this with [WifiRepository].
|
||||
*/
|
||||
@SysUISingleton
|
||||
class NetworkCapabilitiesRepo @Inject constructor(
|
||||
connectivityManager: ConnectivityManager,
|
||||
@Application scope: CoroutineScope,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
) {
|
||||
@SuppressLint("MissingPermission")
|
||||
val dataStream: StateFlow<Map<Int, NetworkCapabilityInfo>> = run {
|
||||
var state = emptyMap<Int, NetworkCapabilityInfo>()
|
||||
callbackFlow {
|
||||
val networkRequest: NetworkRequest =
|
||||
NetworkRequest.Builder()
|
||||
.clearCapabilities()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
.build()
|
||||
val callback =
|
||||
// TODO (b/240569788): log these using [LogBuffer]
|
||||
object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
|
||||
override fun onCapabilitiesChanged(
|
||||
network: Network,
|
||||
networkCapabilities: NetworkCapabilities
|
||||
) {
|
||||
logger.logOnCapabilitiesChanged(network, networkCapabilities)
|
||||
state =
|
||||
state.toMutableMap().also {
|
||||
it[network.getNetId()] =
|
||||
NetworkCapabilityInfo(network, networkCapabilities)
|
||||
}
|
||||
trySend(state)
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
logger.logOnLost(network)
|
||||
state = state.toMutableMap().also { it.remove(network.getNetId()) }
|
||||
trySend(state)
|
||||
}
|
||||
}
|
||||
connectivityManager.registerNetworkCallback(networkRequest, callback)
|
||||
|
||||
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
|
||||
}
|
||||
.stateIn(scope, started = Lazily, initialValue = state)
|
||||
}
|
||||
}
|
||||
|
||||
/** contains info about network capabilities. */
|
||||
data class NetworkCapabilityInfo(
|
||||
val network: Network,
|
||||
val capabilities: NetworkCapabilities,
|
||||
)
|
||||
@@ -16,16 +16,26 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
|
||||
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.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
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.wifi.data.model.WifiActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -38,9 +48,9 @@ import kotlinx.coroutines.flow.flowOf
|
||||
*/
|
||||
interface WifiRepository {
|
||||
/**
|
||||
* Observable for the current state of wifi; `null` when there is no active wifi.
|
||||
* Observable for the current wifi network.
|
||||
*/
|
||||
val wifiModel: Flow<WifiModel?>
|
||||
val wifiNetwork: Flow<WifiNetworkModel>
|
||||
|
||||
/**
|
||||
* Observable for the current wifi network activity.
|
||||
@@ -51,14 +61,57 @@ interface WifiRepository {
|
||||
/** Real implementation of [WifiRepository]. */
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SysUISingleton
|
||||
@SuppressLint("MissingPermission")
|
||||
class WifiRepositoryImpl @Inject constructor(
|
||||
connectivityManager: ConnectivityManager,
|
||||
wifiManager: WifiManager?,
|
||||
@Main mainExecutor: Executor,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
) : WifiRepository {
|
||||
override val wifiNetwork: Flow<WifiNetworkModel> = conflatedCallbackFlow {
|
||||
var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
|
||||
|
||||
// TODO(b/238425913): Actually implement the wifiModel flow.
|
||||
override val wifiModel: Flow<WifiModel?> = flowOf(WifiModel(ssid = "AB"))
|
||||
val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
|
||||
override fun onCapabilitiesChanged(
|
||||
network: Network,
|
||||
networkCapabilities: NetworkCapabilities
|
||||
) {
|
||||
logger.logOnCapabilitiesChanged(network, networkCapabilities)
|
||||
|
||||
val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
|
||||
if (wifiInfo?.isPrimary == true) {
|
||||
val wifiNetworkModel = wifiInfoToModel(wifiInfo, network.getNetId())
|
||||
logger.logTransformation(
|
||||
WIFI_NETWORK_CALLBACK_NAME,
|
||||
oldValue = currentWifi,
|
||||
newValue = wifiNetworkModel
|
||||
)
|
||||
currentWifi = wifiNetworkModel
|
||||
trySend(wifiNetworkModel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
logger.logOnLost(network)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trySend(WIFI_NETWORK_DEFAULT)
|
||||
connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
|
||||
|
||||
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
|
||||
}
|
||||
|
||||
override val wifiActivity: Flow<WifiActivityModel> =
|
||||
if (wifiManager == null) {
|
||||
@@ -71,8 +124,8 @@ class WifiRepositoryImpl @Inject constructor(
|
||||
trySend(trafficStateToWifiActivityModel(state))
|
||||
}
|
||||
|
||||
wifiManager.registerTrafficStateCallback(mainExecutor, callback)
|
||||
trySend(ACTIVITY_DEFAULT)
|
||||
wifiManager.registerTrafficStateCallback(mainExecutor, callback)
|
||||
|
||||
awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
|
||||
}
|
||||
@@ -80,6 +133,13 @@ class WifiRepositoryImpl @Inject constructor(
|
||||
|
||||
companion object {
|
||||
val ACTIVITY_DEFAULT = WifiActivityModel(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 trafficStateToWifiActivityModel(state: Int): WifiActivityModel {
|
||||
return WifiActivityModel(
|
||||
@@ -90,6 +150,30 @@ class WifiRepositoryImpl @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
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 wifiInfoToModel(wifiInfo: WifiInfo, networkId: Int): WifiNetworkModel {
|
||||
return WifiNetworkModel.Active(
|
||||
networkId,
|
||||
wifiInfo.ssid,
|
||||
wifiInfo.isPasspointAp,
|
||||
wifiInfo.isOsuAp,
|
||||
wifiInfo.passpointProviderFriendlyName
|
||||
)
|
||||
}
|
||||
|
||||
private fun prettyPrintActivity(activity: Int): String {
|
||||
return when (activity) {
|
||||
TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
|
||||
@@ -99,5 +183,15 @@ class WifiRepositoryImpl @Inject constructor(
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
|
||||
|
||||
import android.net.wifi.WifiManager
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -34,13 +35,15 @@ import kotlinx.coroutines.flow.map
|
||||
class WifiInteractor @Inject constructor(
|
||||
repository: WifiRepository,
|
||||
) {
|
||||
private val ssid: Flow<String?> = repository.wifiModel.map { info ->
|
||||
when {
|
||||
info == null -> null
|
||||
info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
|
||||
info.passpointProviderFriendlyName
|
||||
info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
|
||||
else -> null
|
||||
private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
|
||||
when (info) {
|
||||
is WifiNetworkModel.Inactive -> null
|
||||
is WifiNetworkModel.Active -> when {
|
||||
info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
|
||||
info.passpointProviderFriendlyName
|
||||
info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.net.NetworkCapabilities
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.yield
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class ConnectivityInfoProcessorTest : SysuiTestCase() {
|
||||
|
||||
private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
|
||||
// GIVEN a processor hooked up to a collector
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val collector = FakeConnectivityInfoCollector()
|
||||
val processor = ConnectivityInfoProcessor(
|
||||
collector,
|
||||
context,
|
||||
scope,
|
||||
statusBarPipelineFlags,
|
||||
mock(),
|
||||
)
|
||||
|
||||
var mostRecentValue: ProcessedConnectivityInfo? = null
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
processor.processedInfoFlow.collect {
|
||||
mostRecentValue = it
|
||||
}
|
||||
}
|
||||
|
||||
// WHEN the collector emits a value
|
||||
val networkCapabilityInfo = mapOf(
|
||||
10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
|
||||
)
|
||||
collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
|
||||
// Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
|
||||
// this test. So, our test needs to yield to let the job run.
|
||||
// Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
|
||||
yield()
|
||||
|
||||
// THEN the processor receives it
|
||||
assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
|
||||
* receives in [emitValue].
|
||||
*/
|
||||
class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
|
||||
private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
|
||||
override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
|
||||
|
||||
suspend fun emitValue(value: RawConnectivityInfo) {
|
||||
_rawConnectivityInfoFlow.emit(value)
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,22 @@
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
/** Fake implementation of [WifiRepository] exposing set methods for all the flows. */
|
||||
class FakeWifiRepository : WifiRepository {
|
||||
private val _wifiModel: MutableStateFlow<WifiModel?> = MutableStateFlow(null)
|
||||
override val wifiModel: Flow<WifiModel?> = _wifiModel
|
||||
private val _wifiNetwork: MutableStateFlow<WifiNetworkModel> =
|
||||
MutableStateFlow(WifiNetworkModel.Inactive)
|
||||
override val wifiNetwork: Flow<WifiNetworkModel> = _wifiNetwork
|
||||
|
||||
private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
|
||||
override val wifiActivity: Flow<WifiActivityModel> = _wifiActivity
|
||||
|
||||
fun setWifiModel(wifiModel: WifiModel?) {
|
||||
_wifiModel.value = wifiModel
|
||||
fun setWifiNetwork(wifiNetworkModel: WifiNetworkModel) {
|
||||
_wifiNetwork.value = wifiNetworkModel
|
||||
}
|
||||
|
||||
fun setWifiActivity(activity: WifiActivityModel) {
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
|
||||
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.test.suitebuilder.annotation.SmallTest
|
||||
import android.testing.AndroidTestingRunner
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.withArgCaptor
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
// TODO(b/240619365): Update this test to use `runTest` when we update the testing library
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class NetworkCapabilitiesRepoTest : SysuiTestCase() {
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnCapabilitiesChanged_oneNewNetwork_networkStored() = runBlocking {
|
||||
// GIVEN a repo hooked up to [ConnectivityManager]
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val repo = NetworkCapabilitiesRepo(
|
||||
connectivityManager = connectivityManager,
|
||||
scope = scope,
|
||||
logger = logger,
|
||||
)
|
||||
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
repo.dataStream.collect {
|
||||
}
|
||||
}
|
||||
|
||||
val callback: NetworkCallback = withArgCaptor {
|
||||
verify(connectivityManager)
|
||||
.registerNetworkCallback(any(NetworkRequest::class.java), capture())
|
||||
}
|
||||
|
||||
// WHEN a new network is added
|
||||
callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
|
||||
|
||||
val currentMap = repo.dataStream.value
|
||||
|
||||
// THEN it is emitted from the flow
|
||||
assertThat(currentMap[NET_1_ID]?.network).isEqualTo(NET_1)
|
||||
assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_1_CAPS)
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnCapabilitiesChanged_twoNewNetworks_bothStored() = runBlocking {
|
||||
// GIVEN a repo hooked up to [ConnectivityManager]
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val repo = NetworkCapabilitiesRepo(
|
||||
connectivityManager = connectivityManager,
|
||||
scope = scope,
|
||||
logger = logger,
|
||||
)
|
||||
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
repo.dataStream.collect {
|
||||
}
|
||||
}
|
||||
|
||||
val callback: NetworkCallback = withArgCaptor {
|
||||
verify(connectivityManager)
|
||||
.registerNetworkCallback(any(NetworkRequest::class.java), capture())
|
||||
}
|
||||
|
||||
// WHEN two new networks are added
|
||||
callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
|
||||
callback.onCapabilitiesChanged(NET_2, NET_2_CAPS)
|
||||
|
||||
val currentMap = repo.dataStream.value
|
||||
|
||||
// THEN the current state of the flow reflects 2 networks
|
||||
assertThat(currentMap[NET_1_ID]?.network).isEqualTo(NET_1)
|
||||
assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_1_CAPS)
|
||||
assertThat(currentMap[NET_2_ID]?.network).isEqualTo(NET_2)
|
||||
assertThat(currentMap[NET_2_ID]?.capabilities).isEqualTo(NET_2_CAPS)
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnCapabilitesChanged_newCapabilitiesForExistingNetwork_areCaptured() = runBlocking {
|
||||
// GIVEN a repo hooked up to [ConnectivityManager]
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val repo = NetworkCapabilitiesRepo(
|
||||
connectivityManager = connectivityManager,
|
||||
scope = scope,
|
||||
logger = logger,
|
||||
)
|
||||
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
repo.dataStream.collect {
|
||||
}
|
||||
}
|
||||
|
||||
val callback: NetworkCallback = withArgCaptor {
|
||||
verify(connectivityManager)
|
||||
.registerNetworkCallback(any(NetworkRequest::class.java), capture())
|
||||
}
|
||||
|
||||
// WHEN a network is added, and then its capabilities are changed
|
||||
callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
|
||||
callback.onCapabilitiesChanged(NET_1, NET_2_CAPS)
|
||||
|
||||
val currentMap = repo.dataStream.value
|
||||
|
||||
// THEN the current state of the flow reflects the new capabilities
|
||||
assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_2_CAPS)
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnLost_networkIsRemoved() = runBlocking {
|
||||
// GIVEN a repo hooked up to [ConnectivityManager]
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val repo = NetworkCapabilitiesRepo(
|
||||
connectivityManager = connectivityManager,
|
||||
scope = scope,
|
||||
logger = logger,
|
||||
)
|
||||
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
repo.dataStream.collect {
|
||||
}
|
||||
}
|
||||
|
||||
val callback: NetworkCallback = withArgCaptor {
|
||||
verify(connectivityManager)
|
||||
.registerNetworkCallback(any(NetworkRequest::class.java), capture())
|
||||
}
|
||||
|
||||
// WHEN two new networks are added, and one is removed
|
||||
callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
|
||||
callback.onCapabilitiesChanged(NET_2, NET_2_CAPS)
|
||||
callback.onLost(NET_1)
|
||||
|
||||
val currentMap = repo.dataStream.value
|
||||
|
||||
// THEN the current state of the flow reflects only the remaining network
|
||||
assertThat(currentMap[NET_1_ID]).isNull()
|
||||
assertThat(currentMap[NET_2_ID]?.network).isEqualTo(NET_2)
|
||||
assertThat(currentMap[NET_2_ID]?.capabilities).isEqualTo(NET_2_CAPS)
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnLost_noNetworks_doesNotCrash() = runBlocking {
|
||||
// GIVEN a repo hooked up to [ConnectivityManager]
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
val repo = NetworkCapabilitiesRepo(
|
||||
connectivityManager = connectivityManager,
|
||||
scope = scope,
|
||||
logger = logger,
|
||||
)
|
||||
|
||||
val job = launch(start = CoroutineStart.UNDISPATCHED) {
|
||||
repo.dataStream.collect {
|
||||
}
|
||||
}
|
||||
|
||||
val callback: NetworkCallback = withArgCaptor {
|
||||
verify(connectivityManager)
|
||||
.registerNetworkCallback(any(NetworkRequest::class.java), capture())
|
||||
}
|
||||
|
||||
// WHEN no networks are added, and one is removed
|
||||
callback.onLost(NET_1)
|
||||
|
||||
val currentMap = repo.dataStream.value
|
||||
|
||||
// THEN the current state of the flow shows no networks
|
||||
assertThat(currentMap).isEmpty()
|
||||
|
||||
job.cancel()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
private val NET_1_ID = 100
|
||||
private val NET_1 = mock<Network>().also {
|
||||
whenever(it.getNetId()).thenReturn(NET_1_ID)
|
||||
}
|
||||
private val NET_2_ID = 200
|
||||
private val NET_2 = mock<Network>().also {
|
||||
whenever(it.getNetId()).thenReturn(NET_2_ID)
|
||||
}
|
||||
|
||||
private val NET_1_CAPS = NetworkCapabilities.Builder()
|
||||
.addTransportType(TRANSPORT_CELLULAR)
|
||||
.addCapability(NET_CAPABILITY_VALIDATED)
|
||||
.build()
|
||||
|
||||
private val NET_2_CAPS = NetworkCapabilities.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI)
|
||||
.addCapability(NET_CAPABILITY_NOT_METERED)
|
||||
.addCapability(NET_CAPABILITY_VALIDATED)
|
||||
.build()
|
||||
}
|
||||
@@ -16,16 +16,26 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.net.NetworkCapabilities.TRANSPORT_WIFI
|
||||
import android.net.vcn.VcnTransportInfo
|
||||
import android.net.wifi.WifiInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiManager.TrafficStateCallback
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||
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.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.concurrent.Executor
|
||||
@@ -38,6 +48,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -47,6 +58,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
private lateinit var underTest: WifiRepositoryImpl
|
||||
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var wifiManager: WifiManager
|
||||
private lateinit var executor: Executor
|
||||
|
||||
@@ -54,11 +66,347 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
executor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
underTest = WifiRepositoryImpl(
|
||||
connectivityManager,
|
||||
wifiManager,
|
||||
executor,
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
}
|
||||
val network = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(NETWORK_ID)
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestActive.ssid).isEqualTo(SSID)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(false)
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val capabilities = mock<NetworkCapabilities>().apply {
|
||||
whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
|
||||
whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestActive.ssid).isEqualTo(SSID)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(false)
|
||||
}
|
||||
val capabilities = mock<NetworkCapabilities>().apply {
|
||||
whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
|
||||
whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val capabilities = mock<NetworkCapabilities>().apply {
|
||||
whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
|
||||
whenever(this.transportInfo).thenReturn(mock())
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
// Start with the original network
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
|
||||
|
||||
// WHEN we update to a new primary network
|
||||
val newNetworkId = 456
|
||||
val newNetwork = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(newNetworkId)
|
||||
}
|
||||
val newSsid = "CD"
|
||||
val newWifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(newSsid)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(
|
||||
newNetwork, createWifiNetworkCapabilities(newWifiInfo)
|
||||
)
|
||||
|
||||
// THEN we use the new network
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(newNetworkId)
|
||||
assertThat(latestActive.ssid).isEqualTo(newSsid)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
// Start with the original network
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
|
||||
|
||||
// WHEN we notify of a new but non-primary network
|
||||
val newNetworkId = 456
|
||||
val newNetwork = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(newNetworkId)
|
||||
}
|
||||
val newSsid = "EF"
|
||||
val newWifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(newSsid)
|
||||
whenever(this.isPrimary).thenReturn(false)
|
||||
}
|
||||
|
||||
getNetworkCallback().onCapabilitiesChanged(
|
||||
newNetwork, createWifiNetworkCapabilities(newWifiInfo)
|
||||
)
|
||||
|
||||
// THEN we still use the original network
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestActive.ssid).isEqualTo(SSID)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_newNetworkCapabilities_flowHasNewData() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
val wifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
}
|
||||
|
||||
// Start with the original network
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
|
||||
|
||||
// WHEN we keep the same network ID but change the SSID
|
||||
val newSsid = "CD"
|
||||
val newWifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(newSsid)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
}
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(newWifiInfo))
|
||||
|
||||
// THEN we've updated to the new SSID
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestActive.ssid).isEqualTo(newSsid)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
// WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
|
||||
getNetworkCallback().onLost(NETWORK)
|
||||
|
||||
// THEN there's no crash and we still have no network
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
|
||||
assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
|
||||
|
||||
// WHEN we lose our current network
|
||||
getNetworkCallback().onLost(NETWORK)
|
||||
|
||||
// THEN we update to no network
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
|
||||
assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
|
||||
|
||||
// WHEN we lose an unknown network
|
||||
val unknownNetwork = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(543)
|
||||
}
|
||||
getNetworkCallback().onLost(unknownNetwork)
|
||||
|
||||
// THEN we still have our previous network
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
|
||||
assertThat(latestActive.ssid).isEqualTo(SSID)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() = runBlocking(IMMEDIATE) {
|
||||
var latest: WifiNetworkModel? = null
|
||||
val job = underTest
|
||||
.wifiNetwork
|
||||
.onEach { latest = it }
|
||||
.launchIn(this)
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
|
||||
assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
|
||||
|
||||
// WHEN we update to a new network...
|
||||
val newNetworkId = 89
|
||||
val newNetwork = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(newNetworkId)
|
||||
}
|
||||
getNetworkCallback().onCapabilitiesChanged(
|
||||
newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
|
||||
)
|
||||
// ...and lose the old network
|
||||
getNetworkCallback().onLost(NETWORK)
|
||||
|
||||
// THEN we still have the new network
|
||||
assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
|
||||
underTest = WifiRepositoryImpl(
|
||||
connectivityManager,
|
||||
wifiManager = null,
|
||||
executor,
|
||||
logger,
|
||||
@@ -77,12 +425,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
|
||||
underTest = WifiRepositoryImpl(
|
||||
wifiManager,
|
||||
executor,
|
||||
logger,
|
||||
)
|
||||
|
||||
var latest: WifiActivityModel? = null
|
||||
val job = underTest
|
||||
.wifiActivity
|
||||
@@ -100,12 +442,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
|
||||
underTest = WifiRepositoryImpl(
|
||||
wifiManager,
|
||||
executor,
|
||||
logger,
|
||||
)
|
||||
|
||||
var latest: WifiActivityModel? = null
|
||||
val job = underTest
|
||||
.wifiActivity
|
||||
@@ -123,12 +459,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
|
||||
underTest = WifiRepositoryImpl(
|
||||
wifiManager,
|
||||
executor,
|
||||
logger,
|
||||
)
|
||||
|
||||
var latest: WifiActivityModel? = null
|
||||
val job = underTest
|
||||
.wifiActivity
|
||||
@@ -146,12 +476,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
|
||||
underTest = WifiRepositoryImpl(
|
||||
wifiManager,
|
||||
executor,
|
||||
logger,
|
||||
)
|
||||
|
||||
var latest: WifiActivityModel? = null
|
||||
val job = underTest
|
||||
.wifiActivity
|
||||
@@ -170,6 +494,30 @@ class WifiRepositoryImplTest : SysuiTestCase() {
|
||||
verify(wifiManager).registerTrafficStateCallback(any(), callbackCaptor.capture())
|
||||
return callbackCaptor.value!!
|
||||
}
|
||||
|
||||
private fun getNetworkCallback(): ConnectivityManager.NetworkCallback {
|
||||
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
|
||||
verify(connectivityManager).registerNetworkCallback(any(), callbackCaptor.capture())
|
||||
return callbackCaptor.value!!
|
||||
}
|
||||
|
||||
private fun createWifiNetworkCapabilities(wifiInfo: WifiInfo) =
|
||||
mock<NetworkCapabilities>().apply {
|
||||
whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
|
||||
whenever(this.transportInfo).thenReturn(wifiInfo)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val NETWORK_ID = 45
|
||||
val NETWORK = mock<Network>().apply {
|
||||
whenever(this.getNetId()).thenReturn(NETWORK_ID)
|
||||
}
|
||||
const val SSID = "AB"
|
||||
val PRIMARY_WIFI_INFO: WifiInfo = mock<WifiInfo>().apply {
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -47,7 +47,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
|
||||
|
||||
var latest: Boolean? = null
|
||||
@@ -63,7 +63,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
|
||||
|
||||
var latest: Boolean? = null
|
||||
@@ -79,7 +79,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||
|
||||
var latest: Boolean? = null
|
||||
@@ -95,7 +95,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
||||
|
||||
var latest: Boolean? = null
|
||||
@@ -111,7 +111,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = null))
|
||||
repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
|
||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
||||
|
||||
var latest: Boolean? = null
|
||||
@@ -127,7 +127,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||
|
||||
var latest: Boolean? = null
|
||||
val job = underTest
|
||||
@@ -158,6 +158,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB")
|
||||
}
|
||||
}
|
||||
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
|
||||
@@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
|
||||
@@ -61,7 +61,7 @@ class WifiViewModelTest : SysuiTestCase() {
|
||||
)
|
||||
|
||||
// Set up with a valid SSID
|
||||
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||
repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = "AB"))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user