/* * Copyright (C) 2016 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.server.wifi; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.content.Context; import android.content.res.Resources; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiInfo; import android.os.SystemClock; import android.test.suitebuilder.annotation.SmallTest; import android.util.LocalLog; import android.util.Pair; import com.android.internal.R; import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * Unit tests for {@link com.android.server.wifi.WifiNetworkSelector}. */ @SmallTest public class WifiNetworkSelectorTest { /** Sets up test. */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); setupContext(); setupResources(); setupWifiConfigManager(); setupWifiInfo(); mLocalLog = new LocalLog(512); mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock, mLocalLog); mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator, 1); mDummyEvaluator.setEvaluatorToSelectCandidate(true); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()); mThresholdMinimumRssi2G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); mThresholdMinimumRssi5G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); mThresholdQualifiedRssi2G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz); mThresholdQualifiedRssi5G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz); mStayOnNetworkMinimumTxRate = mResource.getInteger( R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network); mStayOnNetworkMinimumRxRate = mResource.getInteger( R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network); mThresholdSaturatedRssi2G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz); mThresholdSaturatedRssi5G = mResource.getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz); } /** Cleans up test. */ @After public void cleanup() { validateMockitoUsage(); } /** * All this dummy network evaluator does is to pick the very first network * in the scan results. */ public class DummyNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator { private static final String NAME = "DummyNetworkEvaluator"; private boolean mEvaluatorShouldSelectCandidate = true; @Override public String getName() { return NAME; } @Override public void update(List scanDetails) {} /** * Sets whether the evaluator should return a candidate for connection or null. */ public void setEvaluatorToSelectCandidate(boolean shouldSelectCandidate) { mEvaluatorShouldSelectCandidate = shouldSelectCandidate; } /** * This NetworkEvaluator can be configured to return a candidate or null. If returning a * candidate, the first entry in the provided scanDetails will be selected. This requires * that the mock WifiConfigManager be set up to return a WifiConfiguration for the first * scanDetail entry, through * {@link WifiNetworkSelectorTestUtil#setupScanDetailsAndConfigStore}. */ @Override public WifiConfiguration evaluateNetworks(List scanDetails, WifiConfiguration currentNetwork, String currentBssid, boolean connected, boolean untrustedNetworkAllowed, List> connectableNetworks) { if (!mEvaluatorShouldSelectCandidate) { return null; } ScanDetail scanDetail = scanDetails.get(0); mWifiConfigManager.setNetworkCandidateScanResult(0, scanDetail.getScanResult(), 100); assertNotNull("Saved network must not be null", mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail)); return mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail); } } private WifiNetworkSelector mWifiNetworkSelector = null; private DummyNetworkEvaluator mDummyEvaluator = new DummyNetworkEvaluator(); @Mock private WifiConfigManager mWifiConfigManager; @Mock private Context mContext; @Mock private Resources mResource; @Mock private WifiInfo mWifiInfo; @Mock private Clock mClock; private LocalLog mLocalLog; private int mThresholdMinimumRssi2G; private int mThresholdMinimumRssi5G; private int mThresholdQualifiedRssi2G; private int mThresholdQualifiedRssi5G; private int mStayOnNetworkMinimumTxRate; private int mStayOnNetworkMinimumRxRate; private int mThresholdSaturatedRssi2G; private int mThresholdSaturatedRssi5G; private void setupContext() { when(mContext.getResources()).thenReturn(mResource); } private void setupResources() { when(mResource.getBoolean( R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz)) .thenReturn(-70); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz)) .thenReturn(-73); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz)) .thenReturn(-82); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz)) .thenReturn(-85); when(mResource.getInteger( R.integer.config_wifi_framework_max_tx_rate_for_full_scan)) .thenReturn(8); when(mResource.getInteger( R.integer.config_wifi_framework_max_rx_rate_for_full_scan)) .thenReturn(8); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz)) .thenReturn(-57); when(mResource.getInteger( R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)) .thenReturn(-60); } private void setupWifiInfo() { // simulate a disconnected state when(mWifiInfo.is24GHz()).thenReturn(true); when(mWifiInfo.is5GHz()).thenReturn(false); when(mWifiInfo.getRssi()).thenReturn(-70); when(mWifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID); when(mWifiInfo.getBSSID()).thenReturn(null); } private void setupWifiConfigManager() { when(mWifiConfigManager.getLastSelectedNetwork()) .thenReturn(WifiConfiguration.INVALID_NETWORK_ID); } /** * No network selection if scan result is empty. * * WifiStateMachine is in disconnected state. * scanDetails is empty. * * Expected behavior: no network recommended by Network Selector */ @Test public void emptyScanResults() { String[] ssids = new String[0]; String[] bssids = new String[0]; int[] freqs = new int[0]; String[] caps = new String[0]; int[] levels = new int[0]; int[] securities = new int[0]; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); } /** * No network selection if the RSSI values in scan result are too low. * * WifiStateMachine is in disconnected state. * scanDetails contains a 2.4GHz and a 5GHz network, but both with RSSI lower than * the threshold * * Expected behavior: no network recommended by Network Selector */ @Test public void verifyMinimumRssiThreshold() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G - 1, mThresholdMinimumRssi5G - 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); } /** * No network selection if WiFi is connected and it is too short from last * network selection. * * WifiStateMachine is in connected state. * scanDetails contains two valid networks. * Perform a network seletion right after the first one. * * Expected behavior: no network recommended by Network Selector */ @Test public void verifyMinimumTimeGapWhenConnected() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection. ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000); // Do another network selection with WSM in CONNECTED state. candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, true, false, false); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); } /** * Perform network selection if WiFi is disconnected even if it is too short from last * network selection. * * WifiStateMachine is in disconnected state. * scanDetails contains two valid networks. * Perform a network seletion right after the first one. * * Expected behavior: the first network is recommended by Network Selector */ @Test public void verifyNoMinimumTimeGapWhenDisconnected() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection. ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); HashSet blacklist = new HashSet(); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000); // Do another network selection with WSM in DISCONNECTED state. candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, chosenScanResult, candidate); } /** * New network selection is performed if the currently connected network * is a open one. * * WifiStateMachine is connected to a open network. * scanDetails contains a valid networks. * Perform a network seletion after the first one. * * Expected behavior: the first network is recommended by Network Selector */ @Test public void openNetworkIsNotSufficient() { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {5180}; String[] caps = {"[ESS]"}; int[] levels = {mThresholdQualifiedRssi5G + 8}; int[] securities = {SECURITY_NONE}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); // connect to test1 mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); when(mWifiInfo.getNetworkId()).thenReturn(0); when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); when(mWifiInfo.is24GHz()).thenReturn(false); when(mWifiInfo.is5GHz()).thenReturn(true); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); // Do another network selection. WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, true, false, false); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, chosenScanResult, candidate); } /** * New network selection is performed if the currently connected network * has low RSSI value. * * WifiStateMachine is connected to a low RSSI 5GHz network. * scanDetails contains a valid networks. * Perform a network seletion after the first one. * * Expected behavior: the first network is recommended by Network Selector */ @Test public void lowRssi5GNetworkIsNotSufficient() { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdQualifiedRssi5G - 2}; int[] securities = {SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); // connect to test1 mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); when(mWifiInfo.getNetworkId()).thenReturn(0); when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); when(mWifiInfo.is24GHz()).thenReturn(false); when(mWifiInfo.is5GHz()).thenReturn(true); when(mWifiInfo.getRssi()).thenReturn(levels[0]); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); // Do another network selection. WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, true, false, false); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, chosenScanResult, candidate); } /** * Blacklisted BSSID is filtered out for network selection. * * WifiStateMachine is disconnected. * scanDetails contains a network which is blacklisted. * * Expected behavior: no network recommended by Network Selector */ @Test public void filterOutBlacklistedBssid() { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdQualifiedRssi5G + 8}; int[] securities = {SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); blacklist.add(bssids[0]); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); } /** * Wifi network selector doesn't recommend any network if the currently connected one * doesn't show up in the scan results. * * WifiStateMachine is under connected state and 2.4GHz test1 is connected. * The second scan results contains only test2 which now has a stronger RSSI than test1. * Test1 is not in the second scan results. * * Expected behavior: no network recommended by Network Selector */ @Test public void noSelectionWhenCurrentNetworkNotInScanResults() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 2457}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 20, mThresholdMinimumRssi2G + 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; // Make a network selection to connect to test1. ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); when(mWifiInfo.getNetworkId()).thenReturn(0); when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); when(mWifiInfo.is24GHz()).thenReturn(true); when(mWifiInfo.is5GHz()).thenReturn(false); when(mWifiInfo.getRssi()).thenReturn(levels[0]); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); // Prepare the second scan results which have no test1. String[] ssidsNew = {"\"test2\""}; String[] bssidsNew = {"6c:f3:7f:ae:8c:f4"}; int[] freqsNew = {2457}; String[] capsNew = {"[WPA2-EAP-CCMP][ESS]"}; int[] levelsNew = {mThresholdMinimumRssi2G + 40}; scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(ssidsNew, bssidsNew, freqsNew, capsNew, levelsNew, mClock); candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, true, false, false); // The second network selection is skipped since current connected network is // missing from the scan results. assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); } /** * Ensures that settings the user connect choice updates the * NetworkSelectionStatus#mConnectChoice for all other WifiConfigurations in range in the last * round of network selection. * * Expected behavior: WifiConfiguration.NetworkSelectionStatus#mConnectChoice is set to * test1's configkey for test2. test3's WifiConfiguration is unchanged. */ @Test public void setUserConnectChoice() { String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"}; int[] freqs = {2437, 5180, 5181}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1, mThresholdMinimumRssi5G + 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); WifiConfiguration selectedWifiConfig = scanDetailsAndConfigs.getWifiConfigs()[0]; selectedWifiConfig.getNetworkSelectionStatus() .setCandidate(scanDetailsAndConfigs.getScanDetails().get(0).getScanResult()); selectedWifiConfig.getNetworkSelectionStatus().setNetworkSelectionStatus( NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); selectedWifiConfig.getNetworkSelectionStatus().setConnectChoice("bogusKey"); WifiConfiguration configInLastNetworkSelection = scanDetailsAndConfigs.getWifiConfigs()[1]; configInLastNetworkSelection.getNetworkSelectionStatus() .setSeenInLastQualifiedNetworkSelection(true); WifiConfiguration configNotInLastNetworkSelection = scanDetailsAndConfigs.getWifiConfigs()[2]; assertTrue(mWifiNetworkSelector.setUserConnectChoice(selectedWifiConfig.networkId)); verify(mWifiConfigManager).updateNetworkSelectionStatus(selectedWifiConfig.networkId, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); verify(mWifiConfigManager).clearNetworkConnectChoice(selectedWifiConfig.networkId); verify(mWifiConfigManager).setNetworkConnectChoice(configInLastNetworkSelection.networkId, selectedWifiConfig.configKey(), mClock.getWallClockMillis()); verify(mWifiConfigManager, never()).setNetworkConnectChoice( configNotInLastNetworkSelection.networkId, selectedWifiConfig.configKey(), mClock.getWallClockMillis()); } /** * If two qualified networks, test1 and test2, are in range when the user selects test2 over * test1, WifiNetworkSelector will override the NetworkSelector's choice to connect to test1 * with test2. * * Expected behavior: test2 is the recommended network */ @Test public void userConnectChoiceOverridesNetworkEvaluators() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; int[] securities = {SECURITY_PSK, SECURITY_PSK}; ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); // DummyEvaluator always selects the first network in the list. WifiConfiguration networkSelectorChoice = scanDetailsAndConfigs.getWifiConfigs()[0]; networkSelectorChoice.getNetworkSelectionStatus() .setSeenInLastQualifiedNetworkSelection(true); WifiConfiguration userChoice = scanDetailsAndConfigs.getWifiConfigs()[1]; userChoice.getNetworkSelectionStatus() .setCandidate(scanDetailsAndConfigs.getScanDetails().get(1).getScanResult()); // With no user choice set, networkSelectorChoice should be chosen. WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); assertTrue(mWifiNetworkSelector.setUserConnectChoice(userChoice.networkId)); // After user connect choice is set, userChoice should override networkSelectorChoice. candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate); } /** * Wifi network selector doesn't recommend any network if the currently connected 2.4Ghz * network is high quality and no 5GHz networks are available * * WifiStateMachine is under connected state and 2.4GHz test1 is connected. * * Expected behavior: no network selection is performed */ @Test public void test2GhzQualifiedNo5GhzAvailable() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); // Do not perform selection on 2GHz if current network is good and no 5GHz available testStayOrTryToSwitch( mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, false /* not open network */, // Should not try to switch. false); } /** * Wifi network selector performs network selection even when the 2Ghz network is high * quality whenever 5Ghz networks are available. * * WifiStateMachine is under connected state and 2.4GHz test1 is connected. * The scan results contain a 5Ghz network, which forces network selection. * Test1 is not in the second scan results. * * Expected behavior: network selection is performed */ @Test public void test2GhzHighQuality5GhzAvailable() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); // When on 2GHz, even with "good" signal strength, run selection if 5GHz available testStayOrTryToSwitch( // Parameters for network1: mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, false /* not open network */, // Parameters for network2: mThresholdQualifiedRssi5G + 1 /* rssi */, true /* a 5G network */, false /* not open network */, // Should try to switch. true); } /** * Wifi network selector performs network selection when connected to a 5Ghz network that * has an insufficient RSSI. * * WifiStateMachine is under connected state and 5GHz test1 is connected. * * Expected behavior: network selection is performed */ @Test public void test5GhzNotQualifiedLowRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G - 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); // Run Selection when the current 5Ghz network has low RSSI. testStayOrTryToSwitch( mThresholdQualifiedRssi5G + 1 /* rssi before connected */, true /* a 5G network */, false /* not open network */, // Should try to switch. true); } /** * Wifi network selector will not run selection when on a 5Ghz network that is of sufficent * Quality (high-enough RSSI). * * WifiStateMachine is under connected state and 5GHz test1 is connected. * * Expected behavior: network selection is not performed */ @Test public void test5GhzQualified() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); // Connected to a high quality 5Ghz network, so the other result is irrelevant testStayOrTryToSwitch( mThresholdQualifiedRssi5G + 1 /* rssi before connected */, true /* a 5G network */, false /* not open network */, // Should not try to switch. false); } /** * New network selection is performed if the currently connected network * band is 2G and there is no sign of streaming traffic. * * Expected behavior: Network Selector perform network selection after connected * to the first one. */ @Test public void band2GNetworkIsNotSufficientWhenNoOngoingTrafficAnd5GhzAvailable() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); testStayOrTryToSwitch( // Parameters for network1: mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, false /* not open network */, // Parameters for network2: mThresholdQualifiedRssi5G + 1 /* rssi */, true /* a 5G network */, false /* not open network */, // Should try to switch. true); } /** * New network selection is performed if the currently connected network * band is 2G with bad rssi. * * Expected behavior: Network Selector perform network selection after connected * to the first one. */ @Test public void band2GNetworkIsNotSufficientWithBadRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G - 1); // No streaming traffic. when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); testStayOrTryToSwitch( mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, false /* not open network */, // Should try to switch. true); } /** * New network selection is not performed if the currently connected 2G network * has good Rssi and sign of streaming tx traffic. * * Expected behavior: Network selector does not perform network selection. */ @Test public void band2GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1); // Streaming traffic when(mWifiInfo.getTxSuccessRatePps()).thenReturn( (double) (mStayOnNetworkMinimumTxRate + 1)); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); testStayOrTryToSwitch( mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, true /* open network */, // Should not try to switch. false); } /** * New network selection is not performed if the currently connected 2G network * has good Rssi and sign of streaming rx traffic. * * Expected behavior: Network selector does not perform network selection. */ @Test public void band2GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1); // Streaming traffic when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn( (double) (mStayOnNetworkMinimumRxRate + 1)); testStayOrTryToSwitch( mThresholdQualifiedRssi2G + 1 /* rssi before connected */, false /* not a 5G network */, true /* open network */, // Should not try to switch. false); } /** * New network selection is not performed if the currently connected 5G network * has good Rssi and sign of streaming tx traffic. * * Expected behavior: Network selector does not perform network selection. */ @Test public void band5GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1); // Streaming traffic when(mWifiInfo.getTxSuccessRatePps()).thenReturn( (double) (mStayOnNetworkMinimumTxRate + 1)); when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0); testStayOrTryToSwitch( mThresholdQualifiedRssi5G + 1 /* rssi before connected */, true /* a 5G network */, true /* open network */, // Should not try to switch. false); } /** * New network selection is not performed if the currently connected 5G network * has good Rssi and sign of streaming rx traffic. * * Expected behavior: Network selector does not perform network selection. */ @Test public void band5GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() { // Rssi after connected. when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1); // Streaming traffic when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0); when(mWifiInfo.getRxSuccessRatePps()).thenReturn( (double) (mStayOnNetworkMinimumRxRate + 1)); testStayOrTryToSwitch( mThresholdQualifiedRssi5G + 1 /* rssi before connected */, true /* a 5G network */, true /* open network */, // Should not try to switch. false); } /** * This is a meta-test that given two scan results of various types, will * determine whether or not network selection should be performed. * * It sets up two networks, connects to the first, and then ensures that * both are available in the scan results for the NetworkSelector. */ private void testStayOrTryToSwitch( int rssiNetwork1, boolean is5GHzNetwork1, boolean isOpenNetwork1, int rssiNetwork2, boolean is5GHzNetwork2, boolean isOpenNetwork2, boolean shouldSelect) { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {is5GHzNetwork1 ? 5180 : 2437, is5GHzNetwork2 ? 5180 : 2437}; String[] caps = {isOpenNetwork1 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]", isOpenNetwork2 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {rssiNetwork1, rssiNetwork2}; int[] securities = {isOpenNetwork1 ? SECURITY_NONE : SECURITY_PSK, isOpenNetwork2 ? SECURITY_NONE : SECURITY_PSK}; testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect); } /** * This is a meta-test that given one scan results, will * determine whether or not network selection should be performed. * * It sets up two networks, connects to the first, and then ensures that * the scan results for the NetworkSelector. */ private void testStayOrTryToSwitch( int rssi, boolean is5GHz, boolean isOpenNetwork, boolean shouldSelect) { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {is5GHz ? 5180 : 2437}; String[] caps = {isOpenNetwork ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {rssi}; int[] securities = {isOpenNetwork ? SECURITY_NONE : SECURITY_PSK}; testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect); } private void testStayOrTryToSwitchImpl(String[] ssids, String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities, boolean shouldSelect) { // Make a network selection to connect to test1. ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List scanDetails = scanDetailsAndConfigs.getScanDetails(); HashSet blacklist = new HashSet(); // DummyNetworkEvaluator always return the first network in the scan results // for connection, so this should connect to the first network. WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork( scanDetails, blacklist, mWifiInfo, false, true, true); assertNotNull("Result should be not null", candidate); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, scanDetails.get(0).getScanResult(), candidate); when(mWifiInfo.getNetworkId()).thenReturn(0); when(mWifiInfo.getBSSID()).thenReturn(bssids[0]); when(mWifiInfo.is24GHz()).thenReturn(!ScanResult.is5GHz(freqs[0])); when(mWifiInfo.is5GHz()).thenReturn(ScanResult.is5GHz(freqs[0])); when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000); candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, true, false, false); // DummyNetworkEvaluator always return the first network in the scan results // for connection, so if nework selection is performed, the first network should // be returned as candidate. if (shouldSelect) { assertNotNull("Result should be not null", candidate); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, scanDetails.get(0).getScanResult(), candidate); } else { assertEquals("Expect null configuration", null, candidate); } } /** * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out * networks that are not open after network selection is made. * * Expected behavior: return open networks only */ @Test public void getfilterOpenUnsavedNetworks_filtersForOpenNetworks() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); HashSet blacklist = new HashSet<>(); mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); List expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); assertEquals("Expect open unsaved networks", expectedOpenUnsavedNetworks, mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); } /** * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out * saved networks after network selection is made. This should return an empty list when there * are no unsaved networks available. * * Expected behavior: return unsaved networks only. Return empty list if there are no unsaved * networks. */ @Test public void getfilterOpenUnsavedNetworks_filtersOutSavedNetworks() { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {2437, 5180}; String[] caps = {"[ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1}; int[] securities = {SECURITY_NONE}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List unSavedScanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); HashSet blacklist = new HashSet<>(); mWifiNetworkSelector.selectNetwork( unSavedScanDetails, blacklist, mWifiInfo, false, true, false); assertEquals("Expect open unsaved networks", unSavedScanDetails, mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock); List savedScanDetails = scanDetailsAndConfigs.getScanDetails(); mWifiNetworkSelector.selectNetwork( savedScanDetails, blacklist, mWifiInfo, false, true, false); // Saved networks are filtered out. assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty()); } /** * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out * bssid blacklisted networks. * * Expected behavior: do not return blacklisted network */ @Test public void getfilterOpenUnsavedNetworks_filtersOutBlacklistedNetworks() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[ESS]", "[ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); HashSet blacklist = new HashSet<>(); blacklist.add(bssids[0]); mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); List expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); assertEquals("Expect open unsaved networks", expectedOpenUnsavedNetworks, mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); } /** * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should return * empty list when there are no open networks after network selection is made. * * Expected behavior: return empty list */ @Test public void getfilterOpenUnsavedNetworks_returnsEmptyListWhenNoOpenNetworksPresent() { String[] ssids = {"\"test1\"", "\"test2\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"}; int[] freqs = {2437, 5180}; String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"}; int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1}; mDummyEvaluator.setEvaluatorToSelectCandidate(false); List scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); HashSet blacklist = new HashSet<>(); mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false); assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty()); } /** * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should return * empty list when no network selection has been made. * * Expected behavior: return empty list */ @Test public void getfilterOpenUnsavedNetworks_returnsEmptyListWhenNoNetworkSelectionMade() { assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty()); } }