diff options
19 files changed, 356 insertions, 14 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt index b5b99a76ded8..b22e09e9ba40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt @@ -19,6 +19,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 com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import kotlinx.coroutines.flow.StateFlow /** Provides data related to the wifi state. */ @@ -45,6 +46,12 @@ interface WifiRepository { val wifiActivity: StateFlow<DataActivityModel> /** + * The list of known wifi networks, per [WifiManager.scanResults]. This list is passively + * updated and does not trigger a scan. + */ + val wifiScanResults: StateFlow<List<WifiScanEntry>> + + /** * Returns true if the device is currently connected to a wifi network with a valid SSID and * false otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt index 80091ac7bdba..ca042e26b20b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt @@ -27,6 +27,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityMod import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -124,4 +125,9 @@ constructor( activeRepo .flatMapLatest { it.wifiActivity } .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiActivity.value) + + override val wifiScanResults: StateFlow<List<WifiScanEntry>> = + activeRepo + .flatMapLatest { it.wifiScanResults } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiScanResults.value) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt index 4b19c3a153ed..152d181bfc16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt @@ -22,6 +22,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActiv import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -55,6 +56,10 @@ constructor( MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false)) override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity + private val _wifiScanResults: MutableStateFlow<List<WifiScanEntry>> = + MutableStateFlow(emptyList()) + override val wifiScanResults: StateFlow<List<WifiScanEntry>> = _wifiScanResults + fun startProcessingCommands() { demoCommandJob = scope.launch { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt index 36c46a90c731..cfdbe4a9bb6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt @@ -21,6 +21,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityMod 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 com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -49,6 +50,9 @@ class DisabledWifiRepository @Inject constructor() : override val wifiActivity: StateFlow<DataActivityModel> = MutableStateFlow(ACTIVITY).asStateFlow() + override val wifiScanResults: StateFlow<List<WifiScanEntry>> = + MutableStateFlow<List<WifiScanEntry>>(emptyList()).asStateFlow() + companion object { private val NETWORK = WifiNetworkModel.Unavailable private val ACTIVITY = DataActivityModel(hasActivityIn = false, hasActivityOut = false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt index f1b98b3972e1..67dd32f473e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt @@ -16,15 +16,21 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod +import android.annotation.SuppressLint +import android.net.wifi.ScanResult import android.net.wifi.WifiManager import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import java.util.concurrent.Executor +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -64,6 +70,34 @@ object WifiRepositoryHelper { ) } + /** + * Creates a flow that listens for new [ScanResult]s from [WifiManager]. Does not request a scan + */ + fun createNetworkScanFlow( + wifiManager: WifiManager, + scope: CoroutineScope, + @Background dispatcher: CoroutineDispatcher, + inputLogger: () -> Unit, + ): StateFlow<List<WifiScanEntry>> { + return conflatedCallbackFlow { + val callback = + object : WifiManager.ScanResultsCallback() { + @SuppressLint("MissingPermission") + override fun onScanResultsAvailable() { + inputLogger.invoke() + trySend(wifiManager.scanResults.toModel()) + } + } + + wifiManager.registerScanResultsCallback(dispatcher.asExecutor(), callback) + + awaitClose { wifiManager.unregisterScanResultsCallback(callback) } + } + .stateIn(scope, SharingStarted.Eagerly, emptyList()) + } + + private fun List<ScanResult>.toModel(): List<WifiScanEntry> = map { WifiScanEntry(it.SSID) } + // TODO(b/292534484): This print should only be done in [MessagePrinter] part of the log buffer. private fun prettyPrintActivity(activity: Int): String { return when (activity) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt index 7c7b58d00e3b..59ef8846e281 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiReposito 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 com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -231,6 +232,14 @@ constructor( logger::logActivity, ) + override val wifiScanResults: StateFlow<List<WifiScanEntry>> = + WifiRepositoryHelper.createNetworkScanFlow( + wifiManager, + scope, + bgDispatcher, + logger::logScanResults + ) + companion object { // Start out with no known wifi network. // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt index d4f40dd03a32..9b404f187c88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt @@ -23,6 +23,7 @@ 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.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -42,6 +43,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRep 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.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive.toHotspotDeviceType +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.wifitrackerlib.HotspotNetworkEntry import com.android.wifitrackerlib.MergedCarrierEntry import com.android.wifitrackerlib.WifiEntry @@ -51,6 +53,7 @@ import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE import com.android.wifitrackerlib.WifiPickerTracker import java.util.concurrent.Executor import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.SharingStarted @@ -73,6 +76,7 @@ constructor( featureFlags: FeatureFlags, @Application private val scope: CoroutineScope, @Main private val mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, private val wifiPickerTrackerFactory: WifiPickerTrackerFactory, private val wifiManager: WifiManager, @WifiTrackerLibInputLog private val inputLogger: LogBuffer, @@ -300,6 +304,14 @@ constructor( this::logActivity, ) + override val wifiScanResults: StateFlow<List<WifiScanEntry>> = + WifiRepositoryHelper.createNetworkScanFlow( + wifiManager, + scope, + bgDispatcher, + this::logScanResults, + ) + private fun logOnWifiEntriesChanged(connectedEntry: WifiEntry?) { inputLogger.log( TAG, @@ -322,6 +334,9 @@ constructor( inputLogger.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "onActivityChanged: $str1" }) } + private fun logScanResults() = + inputLogger.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" }) + /** * Data class storing all the information fetched from [WifiPickerTracker]. * @@ -345,6 +360,7 @@ constructor( private val featureFlags: FeatureFlags, @Application private val scope: CoroutineScope, @Main private val mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, private val wifiPickerTrackerFactory: WifiPickerTrackerFactory, @WifiTrackerLibInputLog private val inputLogger: LogBuffer, @WifiTrackerLibTableLog private val wifiTrackerLibTableLogBuffer: TableLogBuffer, @@ -354,6 +370,7 @@ constructor( featureFlags, scope, mainExecutor, + bgDispatcher, wifiPickerTrackerFactory, wifiManager, inputLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt index 2bce0d1834b6..110e3390e722 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt @@ -17,16 +17,21 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn /** * The business logic layer for the wifi icon. @@ -66,6 +71,7 @@ class WifiInteractorImpl constructor( connectivityRepository: ConnectivityRepository, wifiRepository: WifiRepository, + @Application scope: CoroutineScope, ) : WifiInteractor { override val ssid: Flow<String?> = @@ -96,5 +102,25 @@ constructor( override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map { it.contains(ConnectivitySlot.WIFI) } - override val areNetworksAvailable: StateFlow<Boolean> = MutableStateFlow(false) + override val areNetworksAvailable: StateFlow<Boolean> = + combine( + wifiNetwork, + wifiRepository.wifiScanResults, + ) { currentNetwork, scanResults -> + // We consider networks to be available if the scan results list contains networks + // other than the one that is currently connected + if (scanResults.isEmpty()) { + false + } else if (currentNetwork !is WifiNetworkModel.Active) { + true + } else { + anyNonMatchingNetworkExists(currentNetwork, scanResults) + } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + + private fun anyNonMatchingNetworkExists( + currentNetwork: WifiNetworkModel.Active, + availableNetworks: List<WifiScanEntry> + ): Boolean = availableNetworks.firstOrNull { it.ssid != currentNetwork.ssid } != null } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt index f244376f5a5a..b76bb51bfcb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt @@ -59,6 +59,8 @@ constructor( fun logActivity(activity: String) { buffer.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "Activity: $str1" }) } + + fun logScanResults() = buffer.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" }) } private const val TAG = "WifiInputLog" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt new file mode 100644 index 000000000000..d4a5a0e5cca9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt @@ -0,0 +1,23 @@ +/* + * 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.shared.model + +/** + * Represents a single entry in the scan results callback. Use the [ssid] field to check against + * other networks + */ +data class WifiScanEntry(val ssid: String) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index c658eb43ce10..e537131bad01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -39,6 +39,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; +import com.android.keyguard.TestScopeProvider; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.classifier.FalsingManagerFake; @@ -74,6 +75,8 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import kotlinx.coroutines.test.TestScope; + @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest @@ -110,6 +113,7 @@ public class CastTileTest extends SysuiTestCase { private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter(); private final FakeWifiRepository mWifiRepository = new FakeWifiRepository(); private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); + private final TestScope mTestScope = TestScopeProvider.getTestScope(); private TestableLooper mTestableLooper; private CastTile mCastTile; @@ -123,7 +127,8 @@ public class CastTileTest extends SysuiTestCase { mWifiInteractor = new WifiInteractorImpl( new FakeConnectivityRepository(), - mWifiRepository + mWifiRepository, + mTestScope ); } @@ -141,7 +146,7 @@ public class CastTileTest extends SysuiTestCase { IconState qsIcon = new IconState(false, 0, ""); WifiIndicators indicators = new WifiIndicators( false, mock(IconState.class), - qsIcon, false,false, "", + qsIcon, false, false, "", false, ""); mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); @@ -155,7 +160,7 @@ public class CastTileTest extends SysuiTestCase { IconState qsIcon = new IconState(false, 0, ""); WifiIndicators indicators = new WifiIndicators( true, mock(IconState.class), - qsIcon, false,false, "", + qsIcon, false, false, "", false, ""); mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); @@ -167,7 +172,7 @@ public class CastTileTest extends SysuiTestCase { IconState qsIcon = new IconState(true, 0, ""); WifiIndicators indicators = new WifiIndicators( true, mock(IconState.class), - qsIcon, false,false, "", + qsIcon, false, false, "", false, ""); mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt index 6fdca5f95e57..8150a313abe6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt @@ -40,6 +40,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileV import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock @@ -58,9 +59,10 @@ class InternetTileViewModelTest : SysuiTestCase() { private val connectivityRepository = FakeConnectivityRepository() private val ethernetInteractor = EthernetInteractor(connectivityRepository) private val wifiRepository = FakeWifiRepository() - private val wifiInteractor = WifiInteractorImpl(connectivityRepository, wifiRepository) private val userSetupRepo = FakeUserSetupRepository() private val testScope = TestScope() + private val wifiInteractor = + WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope) private val tableLogBuffer: TableLogBuffer = mock() private val carrierConfigTracker: CarrierConfigTracker = mock() @@ -162,6 +164,7 @@ class InternetTileViewModelTest : SysuiTestCase() { connectivityRepository.setWifiConnected(validated = false) wifiRepository.setIsWifiDefault(true) wifiRepository.setWifiNetwork(networkModel) + wifiRepository.wifiScanResults.value = emptyList() assertThat(latest).isEqualTo(NOT_CONNECTED_NETWORKS_UNAVAILABLE) } @@ -176,15 +179,13 @@ class InternetTileViewModelTest : SysuiTestCase() { connectivityRepository.setWifiConnected(validated = false) wifiRepository.setIsWifiDefault(true) wifiRepository.setWifiNetwork(networkModel) + wifiRepository.wifiScanResults.value = listOf(WifiScanEntry("test 1")) - /* - TODO: enable this once we support areNetworksAvailable assertThat(latest?.secondaryLabel).isNull() assertThat(latest?.secondaryTitle) .isEqualTo(context.getString(R.string.quick_settings_networks_available)) assertThat(latest?.icon).isNull() assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available) - */ } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt index 4f7bb724aae3..106b54891948 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryHelper.ACTIVITY_DEFAULT import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -39,6 +40,9 @@ class FakeWifiRepository : WifiRepository { private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT) override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity + override val wifiScanResults: MutableStateFlow<List<WifiScanEntry>> = + MutableStateFlow(emptyList()) + fun setIsWifiEnabled(enabled: Boolean) { _isWifiEnabled.value = enabled } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index bea1154eeb34..c2e75aa85fcb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -27,6 +27,7 @@ import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.TransportInfo import android.net.VpnTransportInfo import android.net.vcn.VcnTransportInfo +import android.net.wifi.ScanResult import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import android.net.wifi.WifiManager.TrafficStateCallback @@ -45,6 +46,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -1205,6 +1207,58 @@ class WifiRepositoryImplTest : SysuiTestCase() { .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true)) } + @Test + fun wifiScanResults_containsSsidList() = + testScope.runTest { + val latest by collectLastValue(underTest.wifiScanResults) + + val scanResults = + listOf( + ScanResult().also { it.SSID = "ssid 1" }, + ScanResult().also { it.SSID = "ssid 2" }, + ScanResult().also { it.SSID = "ssid 3" }, + ScanResult().also { it.SSID = "ssid 4" }, + ScanResult().also { it.SSID = "ssid 5" }, + ) + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + val expected = + listOf( + WifiScanEntry(ssid = "ssid 1"), + WifiScanEntry(ssid = "ssid 2"), + WifiScanEntry(ssid = "ssid 3"), + WifiScanEntry(ssid = "ssid 4"), + WifiScanEntry(ssid = "ssid 5"), + ) + + assertThat(latest).isEqualTo(expected) + } + + @Test + fun wifiScanResults_updates() = + testScope.runTest { + val latest by collectLastValue(underTest.wifiScanResults) + + var scanResults = + listOf( + ScanResult().also { it.SSID = "ssid 1" }, + ScanResult().also { it.SSID = "ssid 2" }, + ScanResult().also { it.SSID = "ssid 3" }, + ScanResult().also { it.SSID = "ssid 4" }, + ScanResult().also { it.SSID = "ssid 5" }, + ) + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + // New scan representing no results + scanResults = emptyList() + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + assertThat(latest).isEmpty() + } + private fun createRepo(): WifiRepositoryImpl { return WifiRepositoryImpl( fakeBroadcastDispatcher, @@ -1240,6 +1294,13 @@ class WifiRepositoryImplTest : SysuiTestCase() { return callbackCaptor.value!! } + private fun getScanResultsCallback(): WifiManager.ScanResultsCallback { + testScope.runCurrent() + val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>() + verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture()) + return callbackCaptor.value!! + } + private fun createWifiNetworkCapabilities( transportInfo: TransportInfo, isValidated: Boolean = true, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt index 662e36a55b9b..afab6230df5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod +import android.net.wifi.ScanResult import android.net.wifi.WifiManager import android.net.wifi.WifiManager.UNKNOWN_SSID import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo @@ -32,6 +33,7 @@ import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel 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.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -77,6 +79,7 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() { featureFlags, testScope.backgroundScope, executor, + dispatcher, wifiPickerTrackerFactory, wifiManager, logger, @@ -1137,6 +1140,58 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() { .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true)) } + @Test + fun wifiScanResults_containsSsidList() = + testScope.runTest { + val latest by collectLastValue(underTest.wifiScanResults) + + val scanResults = + listOf( + ScanResult().also { it.SSID = "ssid 1" }, + ScanResult().also { it.SSID = "ssid 2" }, + ScanResult().also { it.SSID = "ssid 3" }, + ScanResult().also { it.SSID = "ssid 4" }, + ScanResult().also { it.SSID = "ssid 5" }, + ) + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + val expected = + listOf( + WifiScanEntry(ssid = "ssid 1"), + WifiScanEntry(ssid = "ssid 2"), + WifiScanEntry(ssid = "ssid 3"), + WifiScanEntry(ssid = "ssid 4"), + WifiScanEntry(ssid = "ssid 5"), + ) + + assertThat(latest).isEqualTo(expected) + } + + @Test + fun wifiScanResults_updates() = + testScope.runTest { + val latest by collectLastValue(underTest.wifiScanResults) + + var scanResults = + listOf( + ScanResult().also { it.SSID = "ssid 1" }, + ScanResult().also { it.SSID = "ssid 2" }, + ScanResult().also { it.SSID = "ssid 3" }, + ScanResult().also { it.SSID = "ssid 4" }, + ScanResult().also { it.SSID = "ssid 5" }, + ) + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + // New scan representing no results + scanResults = listOf() + whenever(wifiManager.scanResults).thenReturn(scanResults) + getScanResultsCallback().onScanResultsAvailable() + + assertThat(latest).isEmpty() + } + private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback { testScope.runCurrent() return callbackCaptor.value @@ -1156,6 +1211,13 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() { } } + private fun getScanResultsCallback(): WifiManager.ScanResultsCallback { + testScope.runCurrent() + val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>() + verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture()) + return callbackCaptor.value!! + } + private companion object { const val TITLE = "AB" } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt index 6fe88c100fdc..1db80651bf9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt @@ -21,11 +21,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn @@ -56,7 +58,8 @@ class WifiInteractorImplTest : SysuiTestCase() { fun setUp() { connectivityRepository = FakeConnectivityRepository() wifiRepository = FakeWifiRepository() - underTest = WifiInteractorImpl(connectivityRepository, wifiRepository) + underTest = + WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope) } @Test @@ -300,4 +303,76 @@ class WifiInteractorImplTest : SysuiTestCase() { job.cancel() } + + @Test + fun areNetworksAvailable_noneActive_noResults() = + testScope.runTest { + val latest by collectLastValue(underTest.areNetworksAvailable) + + wifiRepository.wifiScanResults.value = emptyList() + wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive) + + assertThat(latest).isFalse() + } + + @Test + fun areNetworksAvailable_noneActive_nonEmptyResults() = + testScope.runTest { + val latest by collectLastValue(underTest.areNetworksAvailable) + + wifiRepository.wifiScanResults.value = + listOf( + WifiScanEntry(ssid = "ssid 1"), + WifiScanEntry(ssid = "ssid 2"), + WifiScanEntry(ssid = "ssid 3"), + ) + + wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive) + + assertThat(latest).isTrue() + } + + @Test + fun areNetworksAvailable_activeNetwork_resultsIncludeOtherNetworks() = + testScope.runTest { + val latest by collectLastValue(underTest.areNetworksAvailable) + + wifiRepository.wifiScanResults.value = + listOf( + WifiScanEntry(ssid = "ssid 1"), + WifiScanEntry(ssid = "ssid 2"), + WifiScanEntry(ssid = "ssid 3"), + ) + + wifiRepository.setWifiNetwork( + WifiNetworkModel.Active( + ssid = "ssid 2", + networkId = 1, + level = 2, + ) + ) + + assertThat(latest).isTrue() + } + + @Test + fun areNetworksAvailable_activeNetwork_onlyResultIsTheActiveNetwork() = + testScope.runTest { + val latest by collectLastValue(underTest.areNetworksAvailable) + + wifiRepository.wifiScanResults.value = + listOf( + WifiScanEntry(ssid = "ssid 2"), + ) + + wifiRepository.setWifiNetwork( + WifiNetworkModel.Active( + ssid = "ssid 2", + networkId = 1, + level = 2, + ) + ) + + assertThat(latest).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt index 3f499359cc6a..a0d4d1390b2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt @@ -82,8 +82,8 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() { connectivityRepository = FakeConnectivityRepository() wifiRepository = FakeWifiRepository() wifiRepository.setIsWifiEnabled(true) - interactor = WifiInteractorImpl(connectivityRepository, wifiRepository) scope = CoroutineScope(Dispatchers.Unconfined) + interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope) airplaneModeViewModel = AirplaneModeViewModelImpl( AirplaneModeInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt index c8bb28325065..1d1b84c04d71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt @@ -83,8 +83,8 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase connectivityRepository = FakeConnectivityRepository() wifiRepository = FakeWifiRepository() wifiRepository.setIsWifiEnabled(true) - interactor = WifiInteractorImpl(connectivityRepository, wifiRepository) scope = CoroutineScope(IMMEDIATE) + interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope) airplaneModeViewModel = AirplaneModeViewModelImpl( AirplaneModeInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt index bdeba2a2f4c8..5aacc6626eb7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt @@ -74,7 +74,8 @@ class WifiViewModelTest : SysuiTestCase() { connectivityRepository = FakeConnectivityRepository() wifiRepository = FakeWifiRepository() wifiRepository.setIsWifiEnabled(true) - interactor = WifiInteractorImpl(connectivityRepository, wifiRepository) + interactor = + WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope) airplaneModeViewModel = AirplaneModeViewModelImpl( AirplaneModeInteractor( |