1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE;
20import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
21
22import static org.junit.Assert.*;
23import static org.mockito.Mockito.*;
24
25import android.content.Context;
26import android.content.res.Resources;
27import android.net.wifi.ScanResult;
28import android.net.wifi.WifiConfiguration;
29import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
30import android.net.wifi.WifiInfo;
31import android.os.SystemClock;
32import android.test.suitebuilder.annotation.SmallTest;
33import android.util.LocalLog;
34import android.util.Pair;
35
36import com.android.internal.R;
37import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
38
39import org.junit.After;
40import org.junit.Before;
41import org.junit.Test;
42import org.mockito.Mock;
43import org.mockito.MockitoAnnotations;
44
45import java.util.HashSet;
46import java.util.List;
47
48/**
49 * Unit tests for {@link com.android.server.wifi.WifiNetworkSelector}.
50 */
51@SmallTest
52public class WifiNetworkSelectorTest {
53
54    /** Sets up test. */
55    @Before
56    public void setUp() throws Exception {
57        MockitoAnnotations.initMocks(this);
58        setupContext();
59        setupResources();
60        setupWifiConfigManager();
61        setupWifiInfo();
62        mLocalLog = new LocalLog(512);
63
64        mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock,
65                mLocalLog);
66        mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator, 1);
67        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
68
69        mThresholdMinimumRssi2G = mResource.getInteger(
70                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
71        mThresholdMinimumRssi5G = mResource.getInteger(
72                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
73        mThresholdQualifiedRssi2G = mResource.getInteger(
74                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
75        mThresholdQualifiedRssi5G = mResource.getInteger(
76                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
77    }
78
79    /** Cleans up test. */
80    @After
81    public void cleanup() {
82        validateMockitoUsage();
83    }
84
85    /**
86     * All this dummy network evaluator does is to pick the very first network
87     * in the scan results.
88     */
89    public class DummyNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
90        private static final String NAME = "DummyNetworkEvaluator";
91
92        @Override
93        public String getName() {
94            return NAME;
95        }
96
97        @Override
98        public void update(List<ScanDetail> scanDetails) {}
99
100        /**
101         * Always return the first network in the scan results for connection.
102         */
103        @Override
104        public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
105                    WifiConfiguration currentNetwork, String currentBssid, boolean connected,
106                    boolean untrustedNetworkAllowed,
107                    List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
108            ScanDetail scanDetail = scanDetails.get(0);
109            mWifiConfigManager.setNetworkCandidateScanResult(0, scanDetail.getScanResult(), 100);
110
111            return mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
112        }
113    }
114
115    private WifiNetworkSelector mWifiNetworkSelector = null;
116    private DummyNetworkEvaluator mDummyEvaluator = new DummyNetworkEvaluator();
117    @Mock private WifiConfigManager mWifiConfigManager;
118    @Mock private Context mContext;
119    @Mock private Resources mResource;
120    @Mock private WifiInfo mWifiInfo;
121    @Mock private Clock mClock;
122    private LocalLog mLocalLog;
123    private int mThresholdMinimumRssi2G;
124    private int mThresholdMinimumRssi5G;
125    private int mThresholdQualifiedRssi2G;
126    private int mThresholdQualifiedRssi5G;
127
128    private void setupContext() {
129        when(mContext.getResources()).thenReturn(mResource);
130    }
131
132    private void setupResources() {
133        when(mResource.getBoolean(
134                R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true);
135        when(mResource.getInteger(
136                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
137                .thenReturn(-70);
138        when(mResource.getInteger(
139                R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
140                .thenReturn(-73);
141        when(mResource.getInteger(
142                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
143                .thenReturn(-82);
144        when(mResource.getInteger(
145                R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
146                .thenReturn(-85);
147    }
148
149    private void setupWifiInfo() {
150        // simulate a disconnected state
151        when(mWifiInfo.is24GHz()).thenReturn(true);
152        when(mWifiInfo.is5GHz()).thenReturn(false);
153        when(mWifiInfo.getRssi()).thenReturn(-70);
154        when(mWifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
155        when(mWifiInfo.getBSSID()).thenReturn(null);
156    }
157
158    private void setupWifiConfigManager() {
159        when(mWifiConfigManager.getLastSelectedNetwork())
160                .thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
161    }
162
163    /**
164     * No network selection if scan result is empty.
165     *
166     * WifiStateMachine is in disconnected state.
167     * scanDetails is empty.
168     *
169     * Expected behavior: no network recommended by Network Selector
170     */
171    @Test
172    public void emptyScanResults() {
173        String[] ssids = new String[0];
174        String[] bssids = new String[0];
175        int[] freqs = new int[0];
176        String[] caps = new String[0];
177        int[] levels = new int[0];
178        int[] securities = new int[0];
179
180        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
181                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
182                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
183        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
184        HashSet<String> blacklist = new HashSet<String>();
185        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
186                blacklist, mWifiInfo, false, true, false);
187        assertEquals("Expect null configuration", null, candidate);
188    }
189
190
191    /**
192     * No network selection if the RSSI values in scan result are too low.
193     *
194     * WifiStateMachine is in disconnected state.
195     * scanDetails contains a 2.4GHz and a 5GHz network, but both with RSSI lower than
196     * the threshold
197     *
198     * Expected behavior: no network recommended by Network Selector
199     */
200    @Test
201    public void verifyMinimumRssiThreshold() {
202        String[] ssids = {"\"test1\"", "\"test2\""};
203        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
204        int[] freqs = {2437, 5180};
205        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
206        int[] levels = {mThresholdMinimumRssi2G - 1, mThresholdMinimumRssi5G - 1};
207        int[] securities = {SECURITY_PSK, SECURITY_PSK};
208
209        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
210                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
211                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
212        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
213        HashSet<String> blacklist = new HashSet<String>();
214        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
215                blacklist, mWifiInfo, false, true, false);
216        assertEquals("Expect null configuration", null, candidate);
217    }
218
219    /**
220     * No network selection if WiFi is connected and it is too short from last
221     * network selection.
222     *
223     * WifiStateMachine is in connected state.
224     * scanDetails contains two valid networks.
225     * Perform a network seletion right after the first one.
226     *
227     * Expected behavior: no network recommended by Network Selector
228     */
229    @Test
230    public void verifyMinimumTimeGapWhenConnected() {
231        String[] ssids = {"\"test1\"", "\"test2\""};
232        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
233        int[] freqs = {2437, 5180};
234        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
235        int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
236        int[] securities = {SECURITY_PSK, SECURITY_PSK};
237
238        // Make a network selection.
239        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
240                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
241                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
242        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
243        HashSet<String> blacklist = new HashSet<String>();
244        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
245                blacklist, mWifiInfo, false, true, false);
246
247        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
248                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000);
249
250        // Do another network selection with WSM in CONNECTED state.
251        candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
252                blacklist, mWifiInfo, true, false, false);
253
254        assertEquals("Expect null configuration", null, candidate);
255    }
256
257    /**
258     * Perform network selection if WiFi is disconnected even if it is too short from last
259     * network selection.
260     *
261     * WifiStateMachine is in disconnected state.
262     * scanDetails contains two valid networks.
263     * Perform a network seletion right after the first one.
264     *
265     * Expected behavior: the first network is recommended by Network Selector
266     */
267    @Test
268    public void verifyNoMinimumTimeGapWhenDisconnected() {
269        String[] ssids = {"\"test1\"", "\"test2\""};
270        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
271        int[] freqs = {2437, 5180};
272        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
273        int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
274        int[] securities = {SECURITY_PSK, SECURITY_PSK};
275
276        // Make a network selection.
277        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
278                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
279                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
280        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
281        WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
282        HashSet<String> blacklist = new HashSet<String>();
283        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
284                blacklist, mWifiInfo, false, true, false);
285        WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
286
287        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
288                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000);
289
290        // Do another network selection with WSM in DISCONNECTED state.
291        candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
292                blacklist, mWifiInfo, false, true, false);
293
294        ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
295        WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
296        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
297                chosenScanResult, candidate);
298    }
299
300    /**
301     * No network selection if the currently connected on is already sufficient.
302     *
303     * WifiStateMachine is connected to a qualified (5G, secure, good RSSI) network.
304     * scanDetails contains a valid network.
305     * Perform a network seletion after the first one.
306     *
307     * Expected behavior: no network recommended by Network Selector
308     */
309    @Test
310    public void noNetworkSelectionWhenCurrentOneIsSufficient() {
311        String[] ssids = {"\"test1\""};
312        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
313        int[] freqs = {5180};
314        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
315        int[] levels = {mThresholdQualifiedRssi5G + 8};
316        int[] securities = {SECURITY_PSK};
317
318        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
319                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
320                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
321        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
322        HashSet<String> blacklist = new HashSet<String>();
323
324        // connect to test1
325        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
326        when(mWifiInfo.getNetworkId()).thenReturn(0);
327        when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
328        when(mWifiInfo.is24GHz()).thenReturn(false);
329
330        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
331                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
332
333        levels[0] = mThresholdQualifiedRssi5G + 20;
334        scanDetailsAndConfigs =
335                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
336                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
337        scanDetails = scanDetailsAndConfigs.getScanDetails();
338
339        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
340                blacklist, mWifiInfo, true, false, false);
341        assertEquals("Expect null configuration", null, candidate);
342    }
343
344    /**
345     * New network selection is performed if the currently connected network
346     * band is 2G.
347     *
348     * WifiStateMachine is connected to a 2G network.
349     * scanDetails contains a valid networks.
350     * Perform a network seletion after the first one.
351     *
352     * Expected behavior: the first network is recommended by Network Selector
353     */
354    @Test
355    public void band2GNetworkIsNotSufficient() {
356        String[] ssids = {"\"test1\""};
357        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
358        int[] freqs = {2470};
359        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
360        int[] levels = {mThresholdQualifiedRssi2G + 8};
361        int[] securities = {SECURITY_PSK};
362
363        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
364                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
365                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
366        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
367        HashSet<String> blacklist = new HashSet<String>();
368        WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
369
370        // connect to test1
371        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
372        when(mWifiInfo.getNetworkId()).thenReturn(0);
373        when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
374        when(mWifiInfo.is24GHz()).thenReturn(true);
375
376        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
377                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
378
379        // Do another network selection.
380        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
381                blacklist, mWifiInfo, true, false, false);
382
383        ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
384        WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
385        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
386                chosenScanResult, candidate);
387    }
388
389
390    /**
391     * New network selection is performed if the currently connected network
392     * is a open one.
393     *
394     * WifiStateMachine is connected to a open network.
395     * scanDetails contains a valid networks.
396     * Perform a network seletion after the first one.
397     *
398     * Expected behavior: the first network is recommended by Network Selector
399     */
400    @Test
401    public void openNetworkIsNotSufficient() {
402        String[] ssids = {"\"test1\""};
403        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
404        int[] freqs = {5180};
405        String[] caps = {"[ESS]"};
406        int[] levels = {mThresholdQualifiedRssi5G + 8};
407        int[] securities = {SECURITY_NONE};
408
409        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
410                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
411                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
412        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
413        HashSet<String> blacklist = new HashSet<String>();
414        WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
415
416        // connect to test1
417        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
418        when(mWifiInfo.getNetworkId()).thenReturn(0);
419        when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
420        when(mWifiInfo.is24GHz()).thenReturn(false);
421        when(mWifiInfo.is5GHz()).thenReturn(true);
422
423        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
424                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
425
426        // Do another network selection.
427        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
428                blacklist, mWifiInfo, true, false, false);
429
430        ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
431        WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
432        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
433                chosenScanResult, candidate);
434    }
435
436    /**
437     * New network selection is performed if the currently connected network
438     * has low RSSI value.
439     *
440     * WifiStateMachine is connected to a low RSSI 5GHz network.
441     * scanDetails contains a valid networks.
442     * Perform a network seletion after the first one.
443     *
444     * Expected behavior: the first network is recommended by Network Selector
445     */
446    @Test
447    public void lowRssi5GNetworkIsNotSufficient() {
448        String[] ssids = {"\"test1\""};
449        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
450        int[] freqs = {5180};
451        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
452        int[] levels = {mThresholdQualifiedRssi5G - 2};
453        int[] securities = {SECURITY_PSK};
454
455        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
456                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
457                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
458        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
459        HashSet<String> blacklist = new HashSet<String>();
460        WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
461
462        // connect to test1
463        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
464        when(mWifiInfo.getNetworkId()).thenReturn(0);
465        when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
466        when(mWifiInfo.is24GHz()).thenReturn(false);
467        when(mWifiInfo.is5GHz()).thenReturn(true);
468        when(mWifiInfo.getRssi()).thenReturn(levels[0]);
469
470        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
471                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
472
473        // Do another network selection.
474        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
475                blacklist, mWifiInfo, true, false, false);
476
477        ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
478        WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
479        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
480                chosenScanResult, candidate);
481    }
482
483    /**
484     * Blacklisted BSSID is filtered out for network selection.
485     *
486     * WifiStateMachine is disconnected.
487     * scanDetails contains a network which is blacklisted.
488     *
489     * Expected behavior: no network recommended by Network Selector
490     */
491    @Test
492    public void filterOutBlacklistedBssid() {
493        String[] ssids = {"\"test1\""};
494        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
495        int[] freqs = {5180};
496        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
497        int[] levels = {mThresholdQualifiedRssi5G + 8};
498        int[] securities = {SECURITY_PSK};
499
500        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
501                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
502                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
503        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
504        HashSet<String> blacklist = new HashSet<String>();
505        blacklist.add(bssids[0]);
506
507        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
508                blacklist, mWifiInfo, false, true, false);
509        assertEquals("Expect null configuration", null, candidate);
510    }
511
512    /**
513     * Wifi network selector doesn't recommend any network if the currently connected one
514     * doesn't show up in the scan results.
515     *
516     * WifiStateMachine is under connected state and 2.4GHz test1 is connected.
517     * The second scan results contains only test2 which now has a stronger RSSI than test1.
518     * Test1 is not in the second scan results.
519     *
520     * Expected behavior: no network recommended by Network Selector
521     */
522    @Test
523    public void noSelectionWhenCurrentNetworkNotInScanResults() {
524        String[] ssids = {"\"test1\"", "\"test2\""};
525        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
526        int[] freqs = {2437, 2457};
527        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
528        int[] levels = {mThresholdMinimumRssi2G + 20, mThresholdMinimumRssi2G + 1};
529        int[] securities = {SECURITY_PSK, SECURITY_PSK};
530
531        // Make a network selection to connect to test1.
532        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
533                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
534                    freqs, caps, levels, securities, mWifiConfigManager, mClock);
535        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
536        HashSet<String> blacklist = new HashSet<String>();
537        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
538                blacklist, mWifiInfo, false, true, false);
539
540        when(mWifiInfo.getNetworkId()).thenReturn(0);
541        when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
542        when(mWifiInfo.is24GHz()).thenReturn(true);
543        when(mWifiInfo.is5GHz()).thenReturn(false);
544        when(mWifiInfo.getRssi()).thenReturn(levels[0]);
545        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
546                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
547
548        // Prepare the second scan results which have no test1.
549        String[] ssidsNew = {"\"test2\""};
550        String[] bssidsNew = {"6c:f3:7f:ae:8c:f4"};
551        int[] freqsNew = {2457};
552        String[] capsNew = {"[WPA2-EAP-CCMP][ESS]"};
553        int[] levelsNew = {mThresholdMinimumRssi2G + 40};
554        scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(ssidsNew, bssidsNew,
555                freqsNew, capsNew, levelsNew, mClock);
556        candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo,
557                true, false, false);
558
559        // The second network selection is skipped since current connected network is
560        // missing from the scan results.
561        assertEquals("Expect null configuration", null, candidate);
562    }
563
564    /**
565     * Ensures that settings the user connect choice updates the
566     * NetworkSelectionStatus#mConnectChoice for all other WifiConfigurations in range in the last
567     * round of network selection.
568     *
569     * Expected behavior: WifiConfiguration.NetworkSelectionStatus#mConnectChoice is set to
570     *                    test1's configkey for test2. test3's WifiConfiguration is unchanged.
571     */
572    @Test
573    public void setUserConnectChoice() {
574        String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
575        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
576        int[] freqs = {2437, 5180, 5181};
577        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
578        int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1,
579                mThresholdMinimumRssi5G + 1};
580        int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
581
582        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
583                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
584                        freqs, caps, levels, securities, mWifiConfigManager, mClock);
585
586        WifiConfiguration selectedWifiConfig = scanDetailsAndConfigs.getWifiConfigs()[0];
587        selectedWifiConfig.getNetworkSelectionStatus()
588                .setCandidate(scanDetailsAndConfigs.getScanDetails().get(0).getScanResult());
589        selectedWifiConfig.getNetworkSelectionStatus().setNetworkSelectionStatus(
590                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
591        selectedWifiConfig.getNetworkSelectionStatus().setConnectChoice("bogusKey");
592
593        WifiConfiguration configInLastNetworkSelection = scanDetailsAndConfigs.getWifiConfigs()[1];
594        configInLastNetworkSelection.getNetworkSelectionStatus()
595                .setSeenInLastQualifiedNetworkSelection(true);
596
597        WifiConfiguration configNotInLastNetworkSelection =
598                scanDetailsAndConfigs.getWifiConfigs()[2];
599
600        assertTrue(mWifiNetworkSelector.setUserConnectChoice(selectedWifiConfig.networkId));
601
602        verify(mWifiConfigManager).updateNetworkSelectionStatus(selectedWifiConfig.networkId,
603                NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
604        verify(mWifiConfigManager).clearNetworkConnectChoice(selectedWifiConfig.networkId);
605        verify(mWifiConfigManager).setNetworkConnectChoice(configInLastNetworkSelection.networkId,
606                selectedWifiConfig.configKey(), mClock.getWallClockMillis());
607        verify(mWifiConfigManager, never()).setNetworkConnectChoice(
608                configNotInLastNetworkSelection.networkId, selectedWifiConfig.configKey(),
609                mClock.getWallClockMillis());
610    }
611
612    /**
613     * If two qualified networks, test1 and test2, are in range when the user selects test2 over
614     * test1, WifiNetworkSelector will override the NetworkSelector's choice to connect to test1
615     * with test2.
616     *
617     * Expected behavior: test2 is the recommended network
618     */
619    @Test
620    public void userConnectChoiceOverridesNetworkEvaluators() {
621        String[] ssids = {"\"test1\"", "\"test2\""};
622        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
623        int[] freqs = {2437, 5180};
624        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
625        int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
626        int[] securities = {SECURITY_PSK, SECURITY_PSK};
627
628        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
629                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
630                        freqs, caps, levels, securities, mWifiConfigManager, mClock);
631        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
632        HashSet<String> blacklist = new HashSet<String>();
633
634        // DummyEvaluator always selects the first network in the list.
635        WifiConfiguration networkSelectorChoice = scanDetailsAndConfigs.getWifiConfigs()[0];
636        networkSelectorChoice.getNetworkSelectionStatus()
637                .setSeenInLastQualifiedNetworkSelection(true);
638
639        WifiConfiguration userChoice = scanDetailsAndConfigs.getWifiConfigs()[1];
640        userChoice.getNetworkSelectionStatus()
641                .setCandidate(scanDetailsAndConfigs.getScanDetails().get(1).getScanResult());
642
643        // With no user choice set, networkSelectorChoice should be chosen.
644        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
645                blacklist, mWifiInfo, false, true, false);
646
647        WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate);
648
649        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
650                + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
651
652        assertTrue(mWifiNetworkSelector.setUserConnectChoice(userChoice.networkId));
653
654        // After user connect choice is set, userChoice should override networkSelectorChoice.
655        candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
656                blacklist, mWifiInfo, false, true, false);
657
658        WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
659    }
660}
661