[Status Bar] Implement WifiRepository interface using WifiTrackerLib.
This CL also adds a flag to switch between using the current WifiRepository and using the WifiRepository based on WifiTrackerLib. Bug: 292534484 Bug: 292533677 Test: Flip WIFI_TRACKER_LIB_FOR_WIFI_ICON flag on -> verify wifi icon generally still works Test: dump WifiTrackerLibInputLog and WifiTrackerLibTableLog -> verify data is included only with flag **on** Test: dump WifiInputLog and WifiTableLog -> verify data is included when flag is on **and** off Test: atest WifiRepositoryViaTrackerLibTest Change-Id: Ib74835e78399c6c5ca3821e4aed09bfc0925fc28
This commit is contained in:
@@ -375,6 +375,10 @@ object Flags {
|
||||
@JvmField
|
||||
val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository")
|
||||
|
||||
// TODO(b/292533677): Tracking Bug
|
||||
val WIFI_TRACKER_LIB_FOR_WIFI_ICON =
|
||||
unreleasedFlag(613, "wifi_tracker_lib_for_wifi_icon")
|
||||
|
||||
// 700 - dialer/calls
|
||||
// TODO(b/254512734): Tracking Bug
|
||||
val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.pipeline.dagger
|
||||
import android.net.wifi.WifiManager
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.log.LogBuffer
|
||||
import com.android.systemui.log.LogBufferFactory
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
@@ -48,9 +50,12 @@ import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStat
|
||||
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryViaTrackerLibDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.DisabledWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryViaTrackerLib
|
||||
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
|
||||
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
|
||||
import dagger.Binds
|
||||
@@ -114,14 +119,19 @@ abstract class StatusBarPipelineModule {
|
||||
impl: CollapsedStatusBarViewBinderImpl
|
||||
): CollapsedStatusBarViewBinder
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WifiRepositoryDagger::class)
|
||||
abstract fun bindWifiRepositoryDagger(impl: WifiRepositoryDagger): CoreStartable
|
||||
|
||||
companion object {
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
fun provideRealWifiRepository(
|
||||
fun provideWifiRepositoryDagger(
|
||||
wifiManager: WifiManager?,
|
||||
disabledWifiRepository: DisabledWifiRepository,
|
||||
wifiRepositoryImplFactory: WifiRepositoryImpl.Factory,
|
||||
): RealWifiRepository {
|
||||
): WifiRepositoryDagger {
|
||||
// If we have a null [WifiManager], then the wifi repository should be permanently
|
||||
// disabled.
|
||||
return if (wifiManager == null) {
|
||||
@@ -131,6 +141,36 @@ abstract class StatusBarPipelineModule {
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
fun provideWifiRepositoryViaTrackerLibDagger(
|
||||
wifiManager: WifiManager?,
|
||||
disabledWifiRepository: DisabledWifiRepository,
|
||||
wifiRepositoryFromTrackerLibFactory: WifiRepositoryViaTrackerLib.Factory,
|
||||
): WifiRepositoryViaTrackerLibDagger {
|
||||
// If we have a null [WifiManager], then the wifi repository should be permanently
|
||||
// disabled.
|
||||
return if (wifiManager == null) {
|
||||
disabledWifiRepository
|
||||
} else {
|
||||
wifiRepositoryFromTrackerLibFactory.create(wifiManager)
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
fun provideRealWifiRepository(
|
||||
wifiRepository: WifiRepositoryDagger,
|
||||
wifiRepositoryFromTrackerLib: WifiRepositoryViaTrackerLibDagger,
|
||||
flags: FeatureFlags,
|
||||
): RealWifiRepository {
|
||||
return if (flags.isEnabled(Flags.WIFI_TRACKER_LIB_FOR_WIFI_ICON)) {
|
||||
wifiRepositoryFromTrackerLib
|
||||
} else {
|
||||
wifiRepository
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
@Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
|
||||
@@ -149,6 +189,14 @@ abstract class StatusBarPipelineModule {
|
||||
return factory.create("WifiInputLog", 50)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
@WifiTrackerLibInputLog
|
||||
fun provideWifiTrackerLibInputLogBuffer(factory: LogBufferFactory): LogBuffer {
|
||||
// WifiTrackerLib is pretty noisy, so give it more room than WifiInputLog.
|
||||
return factory.create("WifiTrackerLibInputLog", 200)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
@WifiTableLog
|
||||
@@ -156,6 +204,13 @@ abstract class StatusBarPipelineModule {
|
||||
return factory.create("WifiTableLog", 100)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
@WifiTrackerLibTableLog
|
||||
fun provideWifiTrackerLibTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
|
||||
return factory.create("WifiTrackerLibTableLog", 100)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
@AirplaneTableLog
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.dagger
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
/** Wifi logs for inputs into [WifiRepositoryViaTrackerLib]. */
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class WifiTrackerLibInputLog
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.dagger
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
/** Wifi logs from [WifiRepositoryViaTrackerLib] in table format. */
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class WifiTrackerLibTableLog
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -42,6 +43,13 @@ interface WifiRepository {
|
||||
val currentNetwork = wifiNetwork.value
|
||||
return currentNetwork is WifiNetworkModel.Active && currentNetwork.hasValidSsid()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Column name to use for [isWifiEnabled] for table logging. */
|
||||
const val COL_NAME_IS_ENABLED = "isEnabled"
|
||||
/** Column name to use for [isWifiDefault] for table logging. */
|
||||
const val COL_NAME_IS_DEFAULT = "isDefault"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,3 +61,8 @@ interface WifiRepository {
|
||||
* repository.
|
||||
*/
|
||||
interface RealWifiRepository : WifiRepository
|
||||
|
||||
/** Used only by Dagger to bind [WifiRepositoryImpl]. */
|
||||
interface WifiRepositoryDagger : RealWifiRepository, CoreStartable
|
||||
/** Used only by Dagger to bind [WifiRepositoryViaTrackerLib]. */
|
||||
interface WifiRepositoryViaTrackerLibDagger : RealWifiRepository
|
||||
|
||||
@@ -18,7 +18,8 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryViaTrackerLibDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -32,7 +33,10 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
* wifi information.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class DisabledWifiRepository @Inject constructor() : RealWifiRepository {
|
||||
class DisabledWifiRepository @Inject constructor() :
|
||||
WifiRepositoryDagger, WifiRepositoryViaTrackerLibDagger {
|
||||
override fun start() {}
|
||||
|
||||
override val isWifiEnabled: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
|
||||
|
||||
override val isWifiDefault: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
|
||||
|
||||
@@ -43,8 +43,10 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityMod
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_DEFAULT
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_ENABLED
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
|
||||
import java.util.concurrent.Executor
|
||||
@@ -64,6 +66,7 @@ import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/** Real implementation of [WifiRepository]. */
|
||||
@@ -81,9 +84,21 @@ constructor(
|
||||
@WifiTableLog wifiTableLogBuffer: TableLogBuffer,
|
||||
@Main mainExecutor: Executor,
|
||||
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||
@Application scope: CoroutineScope,
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val wifiManager: WifiManager,
|
||||
) : RealWifiRepository {
|
||||
) : WifiRepositoryDagger {
|
||||
|
||||
override fun start() {
|
||||
// There are two possible [WifiRepository] implementations: This class (old) and
|
||||
// [WifiRepositoryFromTrackerLib] (new). While we migrate to the new class, we want this old
|
||||
// class to still be running in the background so that we can collect logs and compare
|
||||
// discrepancies. This #start method collects on the flows to ensure that the logs are
|
||||
// collected.
|
||||
scope.launch { isWifiEnabled.collect {} }
|
||||
scope.launch { isWifiDefault.collect {} }
|
||||
scope.launch { wifiNetwork.collect {} }
|
||||
scope.launch { wifiActivity.collect {} }
|
||||
}
|
||||
|
||||
private val wifiStateChangeEvents: Flow<Unit> =
|
||||
broadcastDispatcher
|
||||
@@ -104,7 +119,7 @@ constructor(
|
||||
.logDiffsForTable(
|
||||
wifiTableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "isEnabled",
|
||||
columnName = COL_NAME_IS_ENABLED,
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(
|
||||
@@ -125,7 +140,7 @@ constructor(
|
||||
.logDiffsForTable(
|
||||
wifiTableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "isDefault",
|
||||
columnName = COL_NAME_IS_DEFAULT,
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
|
||||
@@ -234,6 +249,8 @@ constructor(
|
||||
// NetworkCallback inside [wifiNetwork] for our wifi network information.
|
||||
val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
|
||||
|
||||
const val WIFI_STATE_DEFAULT = WifiManager.WIFI_STATE_DISABLED
|
||||
|
||||
private fun createWifiNetworkModel(
|
||||
wifiInfo: WifiInfo,
|
||||
network: Network,
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.net.wifi.WifiManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
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.LogBuffer
|
||||
import com.android.systemui.log.core.LogLevel
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.logDiffsForTable
|
||||
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
|
||||
import com.android.systemui.statusbar.pipeline.dagger.WifiTrackerLibInputLog
|
||||
import com.android.systemui.statusbar.pipeline.dagger.WifiTrackerLibTableLog
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_DEFAULT
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_ENABLED
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryViaTrackerLibDagger
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_STATE_DEFAULT
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
|
||||
import com.android.wifitrackerlib.MergedCarrierEntry
|
||||
import com.android.wifitrackerlib.WifiEntry
|
||||
import com.android.wifitrackerlib.WifiPickerTracker
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* An implementation of [WifiRepository] that uses [com.android.wifitrackerlib] as the source of
|
||||
* truth for wifi information.
|
||||
*
|
||||
* Serves as a possible replacement for [WifiRepositoryImpl]. See b/292534484.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class WifiRepositoryViaTrackerLib
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
@Main private val mainExecutor: Executor,
|
||||
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
|
||||
private val wifiManager: WifiManager,
|
||||
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
|
||||
@WifiTrackerLibTableLog private val wifiTrackerLibTableLogBuffer: TableLogBuffer,
|
||||
) : WifiRepositoryViaTrackerLibDagger, LifecycleOwner {
|
||||
|
||||
override val lifecycle =
|
||||
LifecycleRegistry(this).also {
|
||||
mainExecutor.execute { it.currentState = Lifecycle.State.CREATED }
|
||||
}
|
||||
|
||||
private var wifiPickerTracker: WifiPickerTracker? = null
|
||||
|
||||
private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = run {
|
||||
var current =
|
||||
WifiPickerTrackerInfo(
|
||||
state = WIFI_STATE_DEFAULT,
|
||||
isDefault = false,
|
||||
network = WIFI_NETWORK_DEFAULT,
|
||||
)
|
||||
callbackFlow {
|
||||
val callback =
|
||||
object : WifiPickerTracker.WifiPickerTrackerCallback {
|
||||
override fun onWifiEntriesChanged() {
|
||||
val connectedEntry = wifiPickerTracker?.connectedWifiEntry
|
||||
logOnWifiEntriesChanged(connectedEntry)
|
||||
|
||||
// [WifiPickerTracker.connectedWifiEntry] will return the same instance
|
||||
// but with updated internals. For example, when its validation status
|
||||
// changes from false to true, the same instance is re-used but with the
|
||||
// validated field updated.
|
||||
//
|
||||
// Because it's the same instance, the flow won't re-emit the value
|
||||
// (even though the internals have changed). So, we need to transform it
|
||||
// into our internal model immediately. [toWifiNetworkModel] always
|
||||
// returns a new instance, so the flow is guaranteed to emit.
|
||||
send(
|
||||
newNetwork = connectedEntry?.toWifiNetworkModel()
|
||||
?: WIFI_NETWORK_DEFAULT,
|
||||
newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onWifiStateChanged() {
|
||||
val state = wifiPickerTracker?.wifiState
|
||||
logOnWifiStateChanged(state)
|
||||
send(newState = state ?: WIFI_STATE_DEFAULT)
|
||||
}
|
||||
|
||||
override fun onNumSavedNetworksChanged() {}
|
||||
|
||||
override fun onNumSavedSubscriptionsChanged() {}
|
||||
|
||||
private fun send(
|
||||
newState: Int = current.state,
|
||||
newIsDefault: Boolean = current.isDefault,
|
||||
newNetwork: WifiNetworkModel = current.network,
|
||||
) {
|
||||
val new = WifiPickerTrackerInfo(newState, newIsDefault, newNetwork)
|
||||
current = new
|
||||
trySend(new)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/292591403): [WifiPickerTrackerFactory] currently scans to see all
|
||||
// available wifi networks every 10s. Because SysUI only needs to display the
|
||||
// **connected** network, we don't need scans to be running. We should disable these
|
||||
// scans (ideal) or at least run them very infrequently.
|
||||
wifiPickerTracker = wifiPickerTrackerFactory.create(lifecycle, callback)
|
||||
// The lifecycle must be STARTED in order for the callback to receive events.
|
||||
mainExecutor.execute { lifecycle.currentState = Lifecycle.State.STARTED }
|
||||
awaitClose {
|
||||
mainExecutor.execute { lifecycle.currentState = Lifecycle.State.CREATED }
|
||||
}
|
||||
}
|
||||
// TODO(b/292534484): Update to Eagerly once scans are disabled. (Here and other flows)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), current)
|
||||
}
|
||||
|
||||
override val isWifiEnabled: StateFlow<Boolean> =
|
||||
wifiPickerTrackerInfo
|
||||
.map { it.state == WifiManager.WIFI_STATE_ENABLED }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
wifiTrackerLibTableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = COL_NAME_IS_ENABLED,
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val wifiNetwork: StateFlow<WifiNetworkModel> =
|
||||
wifiPickerTrackerInfo
|
||||
.map { it.network }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
wifiTrackerLibTableLogBuffer,
|
||||
columnPrefix = "",
|
||||
initialValue = WIFI_NETWORK_DEFAULT,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), WIFI_NETWORK_DEFAULT)
|
||||
|
||||
/** Converts WifiTrackerLib's [WifiEntry] into our internal model. */
|
||||
private fun WifiEntry.toWifiNetworkModel(): WifiNetworkModel {
|
||||
if (!this.isPrimaryNetwork) {
|
||||
return WIFI_NETWORK_DEFAULT
|
||||
}
|
||||
return if (this is MergedCarrierEntry) {
|
||||
WifiNetworkModel.CarrierMerged(
|
||||
networkId = NETWORK_ID,
|
||||
// TODO(b/292534484): Fetch the real subscription ID from [MergedCarrierEntry].
|
||||
subscriptionId = TEMP_SUB_ID,
|
||||
level = this.level,
|
||||
// WifiManager APIs to calculate the signal level start from 0, so
|
||||
// maxSignalLevel + 1 represents the total level buckets count.
|
||||
numberOfLevels = wifiManager.maxSignalLevel + 1,
|
||||
)
|
||||
} else {
|
||||
WifiNetworkModel.Active(
|
||||
networkId = NETWORK_ID,
|
||||
isValidated = this.hasInternetAccess(),
|
||||
level = this.level,
|
||||
ssid = this.ssid,
|
||||
// TODO(b/292534484): Fetch the real values from [WifiEntry] (#getTitle might be
|
||||
// appropriate).
|
||||
isPasspointAccessPoint = false,
|
||||
isOnlineSignUpForPasspointAccessPoint = false,
|
||||
passpointProviderFriendlyName = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override val isWifiDefault: StateFlow<Boolean> =
|
||||
wifiPickerTrackerInfo
|
||||
.map { it.isDefault }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
wifiTrackerLibTableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = COL_NAME_IS_DEFAULT,
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
// TODO(b/292534484): Re-use WifiRepositoryImpl code to implement wifi activity since
|
||||
// WifiTrackerLib doesn't expose activity details.
|
||||
override val wifiActivity: StateFlow<DataActivityModel> =
|
||||
MutableStateFlow(DataActivityModel(false, false))
|
||||
|
||||
private fun logOnWifiEntriesChanged(connectedEntry: WifiEntry?) {
|
||||
inputLogger.log(
|
||||
TAG,
|
||||
LogLevel.DEBUG,
|
||||
{ str1 = connectedEntry.toString() },
|
||||
{ "onWifiEntriesChanged. ConnectedEntry=$str1" },
|
||||
)
|
||||
}
|
||||
|
||||
private fun logOnWifiStateChanged(state: Int?) {
|
||||
inputLogger.log(
|
||||
TAG,
|
||||
LogLevel.DEBUG,
|
||||
{ int1 = state ?: -1 },
|
||||
{ "onWifiStateChanged. State=${if (int1 == -1) null else int1}" },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class storing all the information fetched from [WifiPickerTracker].
|
||||
*
|
||||
* Used so that we only register a single callback on [WifiPickerTracker].
|
||||
*/
|
||||
data class WifiPickerTrackerInfo(
|
||||
/** The current wifi state. See [WifiManager.getWifiState]. */
|
||||
val state: Int,
|
||||
/** True if wifi is currently the default connection and false otherwise. */
|
||||
val isDefault: Boolean,
|
||||
/** The currently primary wifi network. */
|
||||
val network: WifiNetworkModel,
|
||||
)
|
||||
|
||||
@SysUISingleton
|
||||
class Factory
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
@Main private val mainExecutor: Executor,
|
||||
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
|
||||
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
|
||||
@WifiTrackerLibTableLog private val wifiTrackerLibTableLogBuffer: TableLogBuffer,
|
||||
) {
|
||||
fun create(wifiManager: WifiManager): WifiRepositoryViaTrackerLib {
|
||||
return WifiRepositoryViaTrackerLib(
|
||||
scope,
|
||||
mainExecutor,
|
||||
wifiPickerTrackerFactory,
|
||||
wifiManager,
|
||||
inputLogger,
|
||||
wifiTrackerLibTableLogBuffer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "WifiTrackerLibInputLog"
|
||||
|
||||
/**
|
||||
* [WifiNetworkModel.Active.networkId] is only used at the repository layer. It's used by
|
||||
* [WifiRepositoryImpl], which tracks the ID in order to correctly apply the framework
|
||||
* callbacks within the repository.
|
||||
*
|
||||
* Since this class does not need to manually apply framework callbacks and since the
|
||||
* network ID is not used beyond the repository, it's safe to use an invalid ID in this
|
||||
* repository.
|
||||
*
|
||||
* The [WifiNetworkModel.Active.networkId] field should be deleted once we've fully migrated
|
||||
* to [WifiRepositoryViaTrackerLib].
|
||||
*/
|
||||
private const val NETWORK_ID = -1
|
||||
|
||||
/**
|
||||
* A temporary subscription ID until WifiTrackerLib exposes a method to fetch the
|
||||
* subscription ID.
|
||||
*
|
||||
* Use -2 because [SubscriptionManager.INVALID_SUBSCRIPTION_ID] is -1.
|
||||
*/
|
||||
private const val TEMP_SUB_ID = -2
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,9 @@ import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/**
|
||||
* Note: Any new tests added here may also need to be added to [WifiRepositoryViaTrackerLibTest].
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
|
||||
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiManager.UNKNOWN_SSID
|
||||
import android.testing.TestableLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.log.LogBuffer
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
|
||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
|
||||
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
|
||||
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.capture
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.android.wifitrackerlib.MergedCarrierEntry
|
||||
import com.android.wifitrackerlib.WifiEntry
|
||||
import com.android.wifitrackerlib.WifiPickerTracker
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Note: Most of these tests are duplicates of [WifiRepositoryImplTest] tests.
|
||||
*
|
||||
* Any new tests added here may also need to be added to [WifiRepositoryImplTest].
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var underTest: WifiRepositoryViaTrackerLib
|
||||
|
||||
private val executor = FakeExecutor(FakeSystemClock())
|
||||
private val logger = LogBuffer("name", maxSize = 100, logcatEchoTracker = mock())
|
||||
private val tableLogger = mock<TableLogBuffer>()
|
||||
private val wifiManager =
|
||||
mock<WifiManager>().apply { whenever(this.maxSignalLevel).thenReturn(10) }
|
||||
private val wifiPickerTrackerFactory = mock<WifiPickerTrackerFactory>()
|
||||
private val wifiPickerTracker = mock<WifiPickerTracker>()
|
||||
|
||||
private val callbackCaptor = argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
|
||||
|
||||
private val dispatcher = StandardTestDispatcher()
|
||||
private val testScope = TestScope(dispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor)))
|
||||
.thenReturn(wifiPickerTracker)
|
||||
underTest = createRepo()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiEnabled_enabled_true() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiEnabled)
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_ENABLED)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiEnabled_enabling_false() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiEnabled)
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_ENABLING)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiEnabled_disabling_true() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiEnabled)
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_DISABLING)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiEnabled_disabled_false() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiEnabled)
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_DISABLED)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiEnabled_respondsToUpdates() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiEnabled)
|
||||
executor.runAllReady()
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_ENABLED)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
whenever(wifiPickerTracker.wifiState).thenReturn(WifiManager.WIFI_STATE_DISABLED)
|
||||
getCallback().onWifiStateChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_initiallyGetsDefault() =
|
||||
testScope.runTest { assertThat(underTest.isWifiDefault.value).isFalse() }
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_wifiNetwork_isTrue() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiDefault)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply { whenever(this.isDefaultNetwork).thenReturn(true) }
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_carrierMerged_isTrue() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiDefault)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isDefaultNetwork).thenReturn(true)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_wifiNetworkNotDefault_isFalse() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiDefault)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply { whenever(this.isDefaultNetwork).thenReturn(false) }
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_carrierMergedNotDefault_isFalse() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiDefault)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isDefaultNetwork).thenReturn(false)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiDefault_noWifiNetwork_isFalse() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.isWifiDefault)
|
||||
|
||||
// First, add a network
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply { whenever(this.isDefaultNetwork).thenReturn(true) }
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
// WHEN the network is lost
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
// THEN we update to false
|
||||
assertThat(latest).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_initiallyGetsDefault() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(3)
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
val latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.level).isEqualTo(3)
|
||||
assertThat(latestActive.ssid).isEqualTo(SSID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(3)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
|
||||
val latestMerged = latest as WifiNetworkModel.CarrierMerged
|
||||
assertThat(latestMerged.level).isEqualTo(3)
|
||||
// numberOfLevels = maxSignalLevel + 1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_isCarrierMerged_getsMaxSignalLevel() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
whenever(wifiManager.maxSignalLevel).thenReturn(5)
|
||||
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
|
||||
val latestMerged = latest as WifiNetworkModel.CarrierMerged
|
||||
// numberOfLevels = maxSignalLevel + 1
|
||||
assertThat(latestMerged.numberOfLevels).isEqualTo(6)
|
||||
}
|
||||
|
||||
/* TODO(b/292534484): Re-enable this test once WifiTrackerLib gives us the subscription ID.
|
||||
@Test
|
||||
fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiInfo =
|
||||
mock<WifiInfo>().apply {
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
whenever(this.isCarrierMerged).thenReturn(true)
|
||||
whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(
|
||||
NETWORK,
|
||||
createWifiNetworkCapabilities(wifiInfo),
|
||||
)
|
||||
|
||||
assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_notValidated_networkNotValidated() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.hasInternetAccess()).thenReturn(false)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_validated_networkValidated() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.hasInternetAccess()).thenReturn(true)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply { whenever(this.isPrimaryNetwork).thenReturn(false) }
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_nonPrimaryCarrierMergedNetworkAdded_flowHasNoNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(false)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
// Start with the original network
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(3)
|
||||
whenever(this.ssid).thenReturn("AB")
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
var latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.level).isEqualTo(3)
|
||||
assertThat(latestActive.ssid).isEqualTo("AB")
|
||||
|
||||
// WHEN we update to a new primary network
|
||||
val newWifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(4)
|
||||
whenever(this.ssid).thenReturn("CD")
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(newWifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
// THEN we use the new network
|
||||
assertThat(latest is WifiNetworkModel.Active).isTrue()
|
||||
latestActive = latest as WifiNetworkModel.Active
|
||||
assertThat(latestActive.level).isEqualTo(4)
|
||||
assertThat(latestActive.ssid).isEqualTo("CD")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
// WHEN we receive a null network without any networks beforehand
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
// THEN there's no crash and we still have no network
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun wifiNetwork_currentActiveNetworkLost_flowHasNoNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat((latest as WifiNetworkModel.Active).ssid).isEqualTo(SSID)
|
||||
|
||||
// WHEN we lose our current network
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
// THEN we update to no network
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
}
|
||||
|
||||
/** Possible regression test for b/278618530. */
|
||||
@Test
|
||||
fun wifiNetwork_currentCarrierMergedNetworkLost_flowHasNoNetwork() =
|
||||
testScope.runTest {
|
||||
val latest by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(3)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
|
||||
assertThat((latest as WifiNetworkModel.CarrierMerged).level).isEqualTo(3)
|
||||
|
||||
// WHEN we lose our current network
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
// THEN we update to no network
|
||||
assertThat(latest is WifiNetworkModel.Inactive).isTrue()
|
||||
}
|
||||
|
||||
/** Regression test for b/244173280. */
|
||||
@Test
|
||||
fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() =
|
||||
testScope.runTest {
|
||||
val latest1 by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.level).thenReturn(1)
|
||||
whenever(this.ssid).thenReturn(SSID)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(latest1 is WifiNetworkModel.Active).isTrue()
|
||||
val latest1Active = latest1 as WifiNetworkModel.Active
|
||||
assertThat(latest1Active.level).isEqualTo(1)
|
||||
assertThat(latest1Active.ssid).isEqualTo(SSID)
|
||||
|
||||
// WHEN we add a second subscriber after having already emitted a value
|
||||
val latest2 by collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
// THEN the second subscribe receives the already-emitted value
|
||||
assertThat(latest2 is WifiNetworkModel.Active).isTrue()
|
||||
val latest2Active = latest2 as WifiNetworkModel.Active
|
||||
assertThat(latest2Active.level).isEqualTo(1)
|
||||
assertThat(latest2Active.ssid).isEqualTo(SSID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_inactiveNetwork_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_nonPrimaryNetwork_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply { whenever(this.isPrimaryNetwork).thenReturn(false) }
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_carrierMergedNetwork_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<MergedCarrierEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
/* TODO(b/292534484): Re-enable this test once WifiTrackerLib gives us the subscription ID.
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_invalidNetwork_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiInfo =
|
||||
mock<WifiInfo>().apply {
|
||||
whenever(this.isPrimary).thenReturn(true)
|
||||
whenever(this.isCarrierMerged).thenReturn(true)
|
||||
whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
|
||||
getNetworkCallback()
|
||||
.onCapabilitiesChanged(
|
||||
NETWORK,
|
||||
createWifiNetworkCapabilities(wifiInfo),
|
||||
)
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_activeNetwork_nullSsid_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.ssid).thenReturn(null)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_activeNetwork_unknownSsid_false() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.ssid).thenReturn(UNKNOWN_SSID)
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_activeNetwork_validSsid_true() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.ssid).thenReturn("fakeSsid")
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWifiConnectedWithValidSsid_activeToInactive_trueToFalse() =
|
||||
testScope.runTest {
|
||||
collectLastValue(underTest.wifiNetwork)
|
||||
|
||||
// Start with active
|
||||
val wifiEntry =
|
||||
mock<WifiEntry>().apply {
|
||||
whenever(this.isPrimaryNetwork).thenReturn(true)
|
||||
whenever(this.ssid).thenReturn("fakeSsid")
|
||||
}
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isTrue()
|
||||
|
||||
// WHEN the network is lost
|
||||
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
|
||||
getCallback().onWifiEntriesChanged()
|
||||
testScope.runCurrent()
|
||||
|
||||
// THEN the isWifiConnected updates
|
||||
assertThat(underTest.isWifiConnectedWithValidSsid()).isFalse()
|
||||
}
|
||||
|
||||
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
|
||||
testScope.runCurrent()
|
||||
return callbackCaptor.value
|
||||
}
|
||||
|
||||
private fun createRepo(): WifiRepositoryViaTrackerLib {
|
||||
return WifiRepositoryViaTrackerLib(
|
||||
testScope.backgroundScope,
|
||||
executor,
|
||||
wifiPickerTrackerFactory,
|
||||
wifiManager,
|
||||
logger,
|
||||
tableLogger,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SSID = "AB"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user