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 android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE;
20import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY;
21
22import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
23import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE;
24
25import static org.junit.Assert.*;
26import static org.mockito.Mockito.*;
27
28import android.app.test.MockAnswerUtil.AnswerWithArguments;
29import android.app.test.TestAlarmManager;
30import android.content.Context;
31import android.content.pm.PackageManager;
32import android.content.res.Resources;
33import android.net.NetworkScoreManager;
34import android.net.wifi.ScanResult;
35import android.net.wifi.ScanResult.InformationElement;
36import android.net.wifi.SupplicantState;
37import android.net.wifi.WifiConfiguration;
38import android.net.wifi.WifiInfo;
39import android.net.wifi.WifiNetworkScoreCache;
40import android.net.wifi.WifiScanner;
41import android.net.wifi.WifiScanner.PnoScanListener;
42import android.net.wifi.WifiScanner.PnoSettings;
43import android.net.wifi.WifiScanner.ScanData;
44import android.net.wifi.WifiScanner.ScanListener;
45import android.net.wifi.WifiScanner.ScanSettings;
46import android.net.wifi.WifiSsid;
47import android.os.Process;
48import android.os.SystemClock;
49import android.os.WorkSource;
50import android.os.test.TestLooper;
51import android.support.test.filters.SmallTest;
52import android.util.LocalLog;
53
54import com.android.internal.R;
55
56import org.junit.After;
57import org.junit.Before;
58import org.junit.Test;
59import org.mockito.ArgumentCaptor;
60import org.mockito.Captor;
61import org.mockito.Mock;
62import org.mockito.MockitoAnnotations;
63
64import java.io.FileDescriptor;
65import java.io.PrintWriter;
66import java.io.StringWriter;
67import java.nio.charset.StandardCharsets;
68import java.util.ArrayList;
69import java.util.HashSet;
70import java.util.List;
71import java.util.stream.Collectors;
72
73/**
74 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
75 */
76@SmallTest
77public class WifiConnectivityManagerTest {
78    /**
79     * Called before each test
80     */
81    @Before
82    public void setUp() throws Exception {
83        MockitoAnnotations.initMocks(this);
84        mResource = mockResource();
85        mAlarmManager = new TestAlarmManager();
86        mContext = mockContext();
87        mLocalLog = new LocalLog(512);
88        mWifiStateMachine = mockWifiStateMachine();
89        mWifiConfigManager = mockWifiConfigManager();
90        mWifiInfo = getWifiInfo();
91        mScanData = mockScanData();
92        mWifiScanner = mockWifiScanner();
93        mWifiConnectivityHelper = mockWifiConnectivityHelper();
94        mWifiNS = mockWifiNetworkSelector();
95        mWifiConnectivityManager = createConnectivityManager();
96        verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject());
97        mWifiConnectivityManager.setWifiEnabled(true);
98        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
99        mFullScanMaxTxPacketRate = mResource.getInteger(
100                R.integer.config_wifi_framework_max_tx_rate_for_full_scan);
101        mFullScanMaxRxPacketRate = mResource.getInteger(
102                R.integer.config_wifi_framework_max_rx_rate_for_full_scan);
103        when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
104    }
105
106    /**
107     * Called after each test
108     */
109    @After
110    public void cleanup() {
111        validateMockitoUsage();
112    }
113
114    private Resources mResource;
115
116    private Context mContext;
117    private TestAlarmManager mAlarmManager;
118    private TestLooper mLooper = new TestLooper();
119    private WifiConnectivityManager mWifiConnectivityManager;
120    private WifiNetworkSelector mWifiNS;
121    private WifiStateMachine mWifiStateMachine;
122    private WifiScanner mWifiScanner;
123    private WifiConnectivityHelper mWifiConnectivityHelper;
124    private ScanData mScanData;
125    private WifiConfigManager mWifiConfigManager;
126    private WifiInfo mWifiInfo;
127    private LocalLog mLocalLog;
128    @Mock private FrameworkFacade mFrameworkFacade;
129    @Mock private NetworkScoreManager mNetworkScoreManager;
130    @Mock private Clock mClock;
131    @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog;
132    @Mock private OpenNetworkNotifier mOpenNetworkNotifier;
133    @Mock private CarrierNetworkNotifier mCarrierNetworkNotifier;
134    @Mock private CarrierNetworkConfig mCarrierNetworkConfig;
135    @Mock private WifiMetrics mWifiMetrics;
136    @Mock private WifiNetworkScoreCache mScoreCache;
137    @Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor;
138    @Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor;
139    @Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor;
140    @Captor ArgumentCaptor<WifiConfigManager.OnSavedNetworkUpdateListener>
141            mSavedNetworkUpdateListenerCaptor;
142    private MockResources mResources;
143    private int mFullScanMaxTxPacketRate;
144    private int mFullScanMaxRxPacketRate;
145
146    private static final int CANDIDATE_NETWORK_ID = 0;
147    private static final String CANDIDATE_SSID = "\"AnSsid\"";
148    private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3";
149    private static final String INVALID_SCAN_RESULT_BSSID = "6c:f3:7f:ae:8c:f4";
150    private static final long CURRENT_SYSTEM_TIME_MS = 1000;
151    private static final int MAX_BSSID_BLACKLIST_SIZE = 16;
152
153
154    Resources mockResource() {
155        Resources resource = mock(Resources.class);
156
157        when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
158        when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
159        when(resource.getBoolean(
160                R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true);
161        when(resource.getInteger(
162                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
163                .thenReturn(-60);
164        when(resource.getInteger(
165                R.integer.config_wifi_framework_current_network_boost)).thenReturn(16);
166        when(resource.getInteger(
167                R.integer.config_wifi_framework_max_tx_rate_for_full_scan)).thenReturn(8);
168        when(resource.getInteger(
169                R.integer.config_wifi_framework_max_rx_rate_for_full_scan)).thenReturn(16);
170        return resource;
171    }
172
173    Context mockContext() {
174        Context context = mock(Context.class);
175
176        when(context.getResources()).thenReturn(mResource);
177        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
178                mAlarmManager.getAlarmManager());
179        when(context.getPackageManager()).thenReturn(mock(PackageManager.class));
180
181        return context;
182    }
183
184    ScanData mockScanData() {
185        ScanData scanData = mock(ScanData.class);
186
187        when(scanData.isAllChannelsScanned()).thenReturn(true);
188
189        return scanData;
190    }
191
192    WifiScanner mockWifiScanner() {
193        WifiScanner scanner = mock(WifiScanner.class);
194        ArgumentCaptor<ScanListener> allSingleScanListenerCaptor =
195                ArgumentCaptor.forClass(ScanListener.class);
196
197        doNothing().when(scanner).registerScanListener(allSingleScanListenerCaptor.capture());
198
199        ScanData[] scanDatas = new ScanData[1];
200        scanDatas[0] = mScanData;
201
202        // do a synchronous answer for the ScanListener callbacks
203        doAnswer(new AnswerWithArguments() {
204            public void answer(ScanSettings settings, ScanListener listener,
205                    WorkSource workSource) throws Exception {
206                listener.onResults(scanDatas);
207            }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
208
209        doAnswer(new AnswerWithArguments() {
210            public void answer(ScanSettings settings, ScanListener listener,
211                    WorkSource workSource) throws Exception {
212                listener.onResults(scanDatas);
213                // WCM processes scan results received via onFullResult (even though they're the
214                // same as onResult for single scans).
215                if (mScanData != null && mScanData.getResults() != null) {
216                    for (int i = 0; i < mScanData.getResults().length; i++) {
217                        allSingleScanListenerCaptor.getValue().onFullResult(
218                                mScanData.getResults()[i]);
219                    }
220                }
221                allSingleScanListenerCaptor.getValue().onResults(scanDatas);
222            }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
223
224        // This unfortunately needs to be a somewhat valid scan result, otherwise
225        // |ScanDetailUtil.toScanDetail| raises exceptions.
226        final ScanResult[] scanResults = new ScanResult[1];
227        scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
228                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps",
229                -78, 2450, 1025, 22, 33, 20, 0, 0, true);
230        scanResults[0].informationElements = new InformationElement[1];
231        scanResults[0].informationElements[0] = new InformationElement();
232        scanResults[0].informationElements[0].id = InformationElement.EID_SSID;
233        scanResults[0].informationElements[0].bytes =
234            CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
235
236        doAnswer(new AnswerWithArguments() {
237            public void answer(ScanSettings settings, PnoSettings pnoSettings,
238                    PnoScanListener listener) throws Exception {
239                listener.onPnoNetworkFound(scanResults);
240            }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject());
241
242        doAnswer(new AnswerWithArguments() {
243            public void answer(ScanSettings settings, PnoSettings pnoSettings,
244                    PnoScanListener listener) throws Exception {
245                listener.onPnoNetworkFound(scanResults);
246            }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject());
247
248        return scanner;
249    }
250
251    WifiConnectivityHelper mockWifiConnectivityHelper() {
252        WifiConnectivityHelper connectivityHelper = mock(WifiConnectivityHelper.class);
253
254        when(connectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
255        when(connectivityHelper.getMaxNumBlacklistBssid()).thenReturn(MAX_BSSID_BLACKLIST_SIZE);
256
257        return connectivityHelper;
258    }
259
260    WifiStateMachine mockWifiStateMachine() {
261        WifiStateMachine stateMachine = mock(WifiStateMachine.class);
262
263        when(stateMachine.isConnected()).thenReturn(false);
264        when(stateMachine.isDisconnected()).thenReturn(true);
265        when(stateMachine.isSupplicantTransientState()).thenReturn(false);
266
267        return stateMachine;
268    }
269
270    WifiNetworkSelector mockWifiNetworkSelector() {
271        WifiNetworkSelector ns = mock(WifiNetworkSelector.class);
272
273        WifiConfiguration candidate = generateWifiConfig(
274                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
275        candidate.BSSID = WifiStateMachine.SUPPLICANT_BSSID_ANY;
276        ScanResult candidateScanResult = new ScanResult();
277        candidateScanResult.SSID = CANDIDATE_SSID;
278        candidateScanResult.BSSID = CANDIDATE_BSSID;
279        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
280
281        when(ns.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
282                anyBoolean(), anyBoolean())).thenReturn(candidate);
283        return ns;
284    }
285
286    WifiInfo getWifiInfo() {
287        WifiInfo wifiInfo = new WifiInfo();
288
289        wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
290        wifiInfo.setBSSID(null);
291        wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
292
293        return wifiInfo;
294    }
295
296    WifiConfigManager mockWifiConfigManager() {
297        WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
298
299        when(wifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null);
300
301        // Pass dummy pno network list, otherwise Pno scan requests will not be triggered.
302        PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID);
303        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>();
304        pnoNetworkList.add(pnoNetwork);
305        when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
306        when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
307        doNothing().when(wifiConfigManager).setOnSavedNetworkUpdateListener(
308                mSavedNetworkUpdateListenerCaptor.capture());
309
310        return wifiConfigManager;
311    }
312
313    WifiConnectivityManager createConnectivityManager() {
314        return new WifiConnectivityManager(mContext,
315                new ScoringParams(mContext),
316                mWifiStateMachine, mWifiScanner,
317                mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper,
318                mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier,
319                mCarrierNetworkConfig, mWifiMetrics, mLooper.getLooper(), mClock, mLocalLog, true,
320                mFrameworkFacade, null, null, null);
321    }
322
323    /**
324     *  Wifi enters disconnected state while screen is on.
325     *
326     * Expected behavior: WifiConnectivityManager calls
327     * WifiStateMachine.startConnectToNetwork() with the
328     * expected candidate network ID and BSSID.
329     */
330    @Test
331    public void enterWifiDisconnectedStateWhenScreenOn() {
332        // Set screen to on
333        mWifiConnectivityManager.handleScreenStateChanged(true);
334
335        // Set WiFi to disconnected state
336        mWifiConnectivityManager.handleConnectionStateChanged(
337                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
338
339        verify(mWifiStateMachine).startConnectToNetwork(
340                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
341    }
342
343    /**
344     *  Wifi enters connected state while screen is on.
345     *
346     * Expected behavior: WifiConnectivityManager calls
347     * WifiStateMachine.startConnectToNetwork() with the
348     * expected candidate network ID and BSSID.
349     */
350    @Test
351    public void enterWifiConnectedStateWhenScreenOn() {
352        // Set screen to on
353        mWifiConnectivityManager.handleScreenStateChanged(true);
354
355        // Set WiFi to connected state
356        mWifiConnectivityManager.handleConnectionStateChanged(
357                WifiConnectivityManager.WIFI_STATE_CONNECTED);
358
359        verify(mWifiStateMachine).startConnectToNetwork(
360                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
361    }
362
363    /**
364     *  Screen turned on while WiFi in disconnected state.
365     *
366     * Expected behavior: WifiConnectivityManager calls
367     * WifiStateMachine.startConnectToNetwork() with the
368     * expected candidate network ID and BSSID.
369     */
370    @Test
371    public void turnScreenOnWhenWifiInDisconnectedState() {
372        // Set WiFi to disconnected state
373        mWifiConnectivityManager.handleConnectionStateChanged(
374                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
375
376        // Set screen to on
377        mWifiConnectivityManager.handleScreenStateChanged(true);
378
379        verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
380                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
381    }
382
383    /**
384     *  Screen turned on while WiFi in connected state.
385     *
386     * Expected behavior: WifiConnectivityManager calls
387     * WifiStateMachine.startConnectToNetwork() with the
388     * expected candidate network ID and BSSID.
389     */
390    @Test
391    public void turnScreenOnWhenWifiInConnectedState() {
392        // Set WiFi to connected state
393        mWifiConnectivityManager.handleConnectionStateChanged(
394                WifiConnectivityManager.WIFI_STATE_CONNECTED);
395
396        // Set screen to on
397        mWifiConnectivityManager.handleScreenStateChanged(true);
398
399        verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
400                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
401    }
402
403    /**
404     *  Screen turned on while WiFi in connected state but
405     *  auto roaming is disabled.
406     *
407     * Expected behavior: WifiConnectivityManager doesn't invoke
408     * WifiStateMachine.startConnectToNetwork() because roaming
409     * is turned off.
410     */
411    @Test
412    public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() {
413        // Turn off auto roaming
414        when(mResource.getBoolean(
415                R.bool.config_wifi_framework_enable_associated_network_selection))
416                .thenReturn(false);
417        mWifiConnectivityManager = createConnectivityManager();
418
419        // Set WiFi to connected state
420        mWifiConnectivityManager.handleConnectionStateChanged(
421                WifiConnectivityManager.WIFI_STATE_CONNECTED);
422
423        // Set screen to on
424        mWifiConnectivityManager.handleScreenStateChanged(true);
425
426        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
427                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
428    }
429
430    /**
431     * Multiple back to back connection attempts within the rate interval should be rate limited.
432     *
433     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
434     * with the expected candidate network ID and BSSID for only the expected number of times within
435     * the given interval.
436     */
437    @Test
438    public void connectionAttemptRateLimitedWhenScreenOff() {
439        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
440        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
441        int numAttempts = 0;
442        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
443
444        mWifiConnectivityManager.handleScreenStateChanged(false);
445
446        // First attempt the max rate number of connections within the rate interval.
447        long currentTimeStamp = 0;
448        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
449            currentTimeStamp += connectionAttemptIntervals;
450            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
451            // Set WiFi to disconnected state to trigger PNO scan
452            mWifiConnectivityManager.handleConnectionStateChanged(
453                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
454            numAttempts++;
455        }
456        // Now trigger another connection attempt before the rate interval, this should be
457        // skipped because we've crossed rate limit.
458        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
459        // Set WiFi to disconnected state to trigger PNO scan
460        mWifiConnectivityManager.handleConnectionStateChanged(
461                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
462
463        // Verify that we attempt to connect upto the rate.
464        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
465                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
466    }
467
468    /**
469     * Multiple back to back connection attempts outside the rate interval should not be rate
470     * limited.
471     *
472     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
473     * with the expected candidate network ID and BSSID for only the expected number of times within
474     * the given interval.
475     */
476    @Test
477    public void connectionAttemptNotRateLimitedWhenScreenOff() {
478        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
479        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
480        int numAttempts = 0;
481        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
482
483        mWifiConnectivityManager.handleScreenStateChanged(false);
484
485        // First attempt the max rate number of connections within the rate interval.
486        long currentTimeStamp = 0;
487        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
488            currentTimeStamp += connectionAttemptIntervals;
489            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
490            // Set WiFi to disconnected state to trigger PNO scan
491            mWifiConnectivityManager.handleConnectionStateChanged(
492                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
493            numAttempts++;
494        }
495        // Now trigger another connection attempt after the rate interval, this should not be
496        // skipped because we should've evicted the older attempt.
497        when(mClock.getElapsedSinceBootMillis()).thenReturn(
498                currentTimeStamp + connectionAttemptIntervals * 2);
499        // Set WiFi to disconnected state to trigger PNO scan
500        mWifiConnectivityManager.handleConnectionStateChanged(
501                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
502        numAttempts++;
503
504        // Verify that all the connection attempts went through
505        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
506                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
507    }
508
509    /**
510     * Multiple back to back connection attempts after a user selection should not be rate limited.
511     *
512     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
513     * with the expected candidate network ID and BSSID for only the expected number of times within
514     * the given interval.
515     */
516    @Test
517    public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() {
518        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
519        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
520        int numAttempts = 0;
521        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
522
523        mWifiConnectivityManager.handleScreenStateChanged(false);
524
525        // First attempt the max rate number of connections within the rate interval.
526        long currentTimeStamp = 0;
527        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
528            currentTimeStamp += connectionAttemptIntervals;
529            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
530            // Set WiFi to disconnected state to trigger PNO scan
531            mWifiConnectivityManager.handleConnectionStateChanged(
532                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
533            numAttempts++;
534        }
535
536        mWifiConnectivityManager.setUserConnectChoice(CANDIDATE_NETWORK_ID);
537        mWifiConnectivityManager.prepareForForcedConnection(CANDIDATE_NETWORK_ID);
538
539        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
540            currentTimeStamp += connectionAttemptIntervals;
541            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
542            // Set WiFi to disconnected state to trigger PNO scan
543            mWifiConnectivityManager.handleConnectionStateChanged(
544                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
545            numAttempts++;
546        }
547
548        // Verify that all the connection attempts went through
549        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
550                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
551    }
552
553    /**
554     *  PNO retry for low RSSI networks.
555     *
556     * Expected behavior: WifiConnectivityManager doubles the low RSSI
557     * network retry delay value after QNS skips the PNO scan results
558     * because of their low RSSI values.
559     */
560    @Test
561    public void pnoRetryForLowRssiNetwork() {
562        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
563                anyBoolean(), anyBoolean())).thenReturn(null);
564
565        // Set screen to off
566        mWifiConnectivityManager.handleScreenStateChanged(false);
567
568        // Get the current retry delay value
569        int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager
570                .getLowRssiNetworkRetryDelay();
571
572        // Set WiFi to disconnected state to trigger PNO scan
573        mWifiConnectivityManager.handleConnectionStateChanged(
574                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
575
576        // Get the retry delay value after QNS didn't select a
577        // network candicate from the PNO scan results.
578        int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager
579                .getLowRssiNetworkRetryDelay();
580
581        assertEquals(lowRssiNetworkRetryDelayStartValue * 2,
582                lowRssiNetworkRetryDelayAfterPnoValue);
583    }
584
585    /**
586     * Ensure that the watchdog bite increments the "Pno bad" metric.
587     *
588     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
589     * a candidate while watchdog single scan did.
590     */
591    @Test
592    public void watchdogBitePnoBadIncrementsMetrics() {
593        // Set screen to off
594        mWifiConnectivityManager.handleScreenStateChanged(false);
595
596        // Set WiFi to disconnected state to trigger PNO scan
597        mWifiConnectivityManager.handleConnectionStateChanged(
598                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
599
600        // Now fire the watchdog alarm and verify the metrics were incremented.
601        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
602        mLooper.dispatchAll();
603
604        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad();
605        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood();
606    }
607
608    /**
609     * Ensure that the watchdog bite increments the "Pno good" metric.
610     *
611     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
612     * a candidate which was the same with watchdog single scan.
613     */
614    @Test
615    public void watchdogBitePnoGoodIncrementsMetrics() {
616        // Qns returns no candidate after watchdog single scan.
617        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
618                anyBoolean(), anyBoolean())).thenReturn(null);
619
620        // Set screen to off
621        mWifiConnectivityManager.handleScreenStateChanged(false);
622
623        // Set WiFi to disconnected state to trigger PNO scan
624        mWifiConnectivityManager.handleConnectionStateChanged(
625                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
626
627        // Now fire the watchdog alarm and verify the metrics were incremented.
628        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
629        mLooper.dispatchAll();
630
631        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood();
632        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad();
633    }
634
635    /**
636     * {@link OpenNetworkNotifier} handles scan results on network selection.
637     *
638     * Expected behavior: ONA handles scan results
639     */
640    @Test
641    public void wifiDisconnected_noConnectionCandidate_openNetworkNotifierScanResultsHandled() {
642        // no connection candidate selected
643        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
644                anyBoolean(), anyBoolean())).thenReturn(null);
645
646        List<ScanDetail> expectedOpenNetworks = new ArrayList<>();
647        expectedOpenNetworks.add(
648                new ScanDetail(
649                        new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
650                                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", -78, 2450,
651                                1025, 22, 33, 20, 0, 0, true), null));
652
653        when(mWifiNS.getFilteredScanDetailsForOpenUnsavedNetworks())
654                .thenReturn(expectedOpenNetworks);
655
656        // Set WiFi to disconnected state to trigger PNO scan
657        mWifiConnectivityManager.handleConnectionStateChanged(
658                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
659
660        verify(mOpenNetworkNotifier).handleScanResults(expectedOpenNetworks);
661    }
662
663    /**
664     * When wifi is connected, {@link OpenNetworkNotifier} handles the Wi-Fi connected behavior.
665     *
666     * Expected behavior: ONA handles connected behavior
667     */
668    @Test
669    public void wifiConnected_openNetworkNotifierHandlesConnection() {
670        // Set WiFi to connected state
671        mWifiConnectivityManager.handleConnectionStateChanged(
672                WifiConnectivityManager.WIFI_STATE_CONNECTED);
673
674        verify(mOpenNetworkNotifier).handleWifiConnected();
675    }
676
677    /**
678     * When wifi is connected, {@link OpenNetworkNotifier} handles connection state
679     * change.
680     *
681     * Expected behavior: ONA does not clear pending notification.
682     */
683    @Test
684    public void wifiDisconnected_openNetworkNotifierDoesNotClearPendingNotification() {
685        // Set WiFi to disconnected state
686        mWifiConnectivityManager.handleConnectionStateChanged(
687                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
688
689        verify(mOpenNetworkNotifier, never()).clearPendingNotification(anyBoolean());
690    }
691
692    /**
693     * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} handles the connection
694     * failure. A failure code that is not {@link WifiMetrics.ConnectionEvent#FAILURE_NONE}
695     * represents a connection failure.
696     *
697     * Expected behavior: ONA handles connection failure.
698     */
699    @Test
700    public void wifiConnectionEndsWithFailure_openNetworkNotifierHandlesConnectionFailure() {
701        mWifiConnectivityManager.handleConnectionAttemptEnded(
702                WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED);
703
704        verify(mOpenNetworkNotifier).handleConnectionFailure();
705    }
706
707    /**
708     * When a Wi-Fi connection attempt ends, {@link OpenNetworkNotifier} does not handle connection
709     * failure after a successful connection. {@link WifiMetrics.ConnectionEvent#FAILURE_NONE}
710     * represents a successful connection.
711     *
712     * Expected behavior: ONA does nothing.
713     */
714    @Test
715    public void wifiConnectionEndsWithSuccess_openNetworkNotifierDoesNotHandleConnectionFailure() {
716        mWifiConnectivityManager.handleConnectionAttemptEnded(
717                WifiMetrics.ConnectionEvent.FAILURE_NONE);
718
719        verify(mOpenNetworkNotifier, never()).handleConnectionFailure();
720    }
721
722    /**
723     * When Wi-Fi is disabled, clear the pending notification and reset notification repeat delay.
724     *
725     * Expected behavior: clear pending notification and reset notification repeat delay
726     * */
727    @Test
728    public void openNetworkNotifierClearsPendingNotificationOnWifiDisabled() {
729        mWifiConnectivityManager.setWifiEnabled(false);
730
731        verify(mOpenNetworkNotifier).clearPendingNotification(true /* resetRepeatDelay */);
732    }
733
734    /**
735     * Verify that the ONA controller tracks screen state changes.
736     */
737    @Test
738    public void openNetworkNotifierTracksScreenStateChanges() {
739        mWifiConnectivityManager.handleScreenStateChanged(false);
740
741        verify(mOpenNetworkNotifier).handleScreenStateChanged(false);
742
743        mWifiConnectivityManager.handleScreenStateChanged(true);
744
745        verify(mOpenNetworkNotifier).handleScreenStateChanged(true);
746    }
747
748    /**
749     * {@link CarrierNetworkNotifier} handles scan results on network selection.
750     *
751     * Expected behavior: CarrierNetworkNotifier handles scan results
752     */
753    @Test
754    public void wifiDisconnected_noConnectionCandidate_CarrierNetworkNotifierScanResultsHandled() {
755        // no connection candidate selected
756        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
757                anyBoolean(), anyBoolean())).thenReturn(null);
758
759        List<ScanDetail> expectedCarrierNetworks = new ArrayList<>();
760        expectedCarrierNetworks.add(
761                new ScanDetail(
762                        new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
763                                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "[EAP][ESS]", -78, 2450,
764                                1025, 22, 33, 20, 0, 0, true), null));
765
766        when(mWifiNS.getFilteredScanDetailsForCarrierUnsavedNetworks(any()))
767                .thenReturn(expectedCarrierNetworks);
768
769        // Set WiFi to disconnected state to trigger PNO scan
770        mWifiConnectivityManager.handleConnectionStateChanged(
771                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
772
773        verify(mCarrierNetworkNotifier).handleScanResults(expectedCarrierNetworks);
774    }
775
776    /**
777     * {@link CarrierNetworkNotifier} does not handle scan results on network selection if carrier
778     * encryption info is not available.
779     *
780     * Expected behavior: CarrierNetworkNotifier does not handle scan results
781     */
782    @Test
783    public void whenNoEncryptionInfoAvailable_CarrierNetworkNotifierDoesNotHandleScanResults() {
784        // no connection candidate selected
785        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
786                anyBoolean(), anyBoolean())).thenReturn(null);
787
788        List<ScanDetail> expectedCarrierNetworks = new ArrayList<>();
789        expectedCarrierNetworks.add(
790                new ScanDetail(
791                        new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
792                                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "[EAP][ESS]", -78, 2450,
793                                1025, 22, 33, 20, 0, 0, true), null));
794
795        when(mWifiNS.getFilteredScanDetailsForCarrierUnsavedNetworks(any()))
796                .thenReturn(expectedCarrierNetworks);
797        when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(false);
798
799        // Set WiFi to disconnected state to trigger PNO scan
800        mWifiConnectivityManager.handleConnectionStateChanged(
801                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
802
803        verify(mCarrierNetworkNotifier, never()).handleScanResults(expectedCarrierNetworks);
804    }
805
806    /**
807     * When wifi is connected, {@link CarrierNetworkNotifier} handles the Wi-Fi connected behavior.
808     *
809     * Expected behavior: CarrierNetworkNotifier handles connected behavior
810     */
811    @Test
812    public void wifiConnected_carrierNetworkNotifierHandlesConnection() {
813        // Set WiFi to connected state
814        mWifiConnectivityManager.handleConnectionStateChanged(
815                WifiConnectivityManager.WIFI_STATE_CONNECTED);
816
817        verify(mCarrierNetworkNotifier).handleWifiConnected();
818    }
819
820    /**
821     * When wifi is connected, {@link CarrierNetworkNotifier} handles connection state
822     * change.
823     *
824     * Expected behavior: CarrierNetworkNotifer does not clear pending notification.
825     */
826    @Test
827    public void wifiDisconnected_carrierNetworkNotifierDoesNotClearPendingNotification() {
828        // Set WiFi to disconnected state
829        mWifiConnectivityManager.handleConnectionStateChanged(
830                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
831
832        verify(mCarrierNetworkNotifier, never()).clearPendingNotification(anyBoolean());
833    }
834
835    /**
836     * When a Wi-Fi connection attempt ends, {@link CarrierNetworkNotifier} handles the connection
837     * failure. A failure code that is not {@link WifiMetrics.ConnectionEvent#FAILURE_NONE}
838     * represents a connection failure.
839     *
840     * Expected behavior: CarrierNetworkNotifier handles connection failure.
841     */
842    @Test
843    public void wifiConnectionEndsWithFailure_carrierNetworkNotifierHandlesConnectionFailure() {
844        mWifiConnectivityManager.handleConnectionAttemptEnded(
845                WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED);
846
847        verify(mCarrierNetworkNotifier).handleConnectionFailure();
848    }
849
850    /**
851     * When a Wi-Fi connection attempt ends, {@link CarrierNetworkNotifier} does not handle
852     * connection failure after a successful connection.
853     * {@link WifiMetrics.ConnectionEvent#FAILURE_NONE} represents a successful connection.
854     *
855     * Expected behavior: CarrierNetworkNotifier does nothing.
856     */
857    @Test
858    public void
859            wifiConnectionEndsWithSuccess_carrierNetworkNotifierDoesNotHandleConnectionFailure() {
860        mWifiConnectivityManager.handleConnectionAttemptEnded(
861                WifiMetrics.ConnectionEvent.FAILURE_NONE);
862
863        verify(mCarrierNetworkNotifier, never()).handleConnectionFailure();
864    }
865
866    /**
867     * When Wi-Fi is disabled, clear the pending notification and reset notification repeat delay.
868     *
869     * Expected behavior: clear pending notification and reset notification repeat delay
870     * */
871    @Test
872    public void carrierNetworkNotifierClearsPendingNotificationOnWifiDisabled() {
873        mWifiConnectivityManager.setWifiEnabled(false);
874
875        verify(mCarrierNetworkNotifier).clearPendingNotification(true /* resetRepeatDelay */);
876    }
877
878    /**
879     * Verify that the CarrierNetworkNotifier tracks screen state changes.
880     */
881    @Test
882    public void carrierNetworkNotifierTracksScreenStateChanges() {
883        mWifiConnectivityManager.handleScreenStateChanged(false);
884
885        verify(mCarrierNetworkNotifier).handleScreenStateChanged(false);
886
887        mWifiConnectivityManager.handleScreenStateChanged(true);
888
889        verify(mCarrierNetworkNotifier).handleScreenStateChanged(true);
890    }
891
892    /**
893     *  Verify that scan interval for screen on and wifi disconnected scenario
894     *  is in the exponential backoff fashion.
895     *
896     * Expected behavior: WifiConnectivityManager doubles periodic
897     * scan interval.
898     */
899    @Test
900    public void checkPeriodicScanIntervalWhenDisconnected() {
901        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
902        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
903
904        // Set screen to ON
905        mWifiConnectivityManager.handleScreenStateChanged(true);
906
907        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
908        // by screen state change can settle
909        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
910        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
911
912        // Set WiFi to disconnected state to trigger periodic scan
913        mWifiConnectivityManager.handleConnectionStateChanged(
914                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
915
916        // Get the first periodic scan interval
917        long firstIntervalMs = mAlarmManager
918                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
919                - currentTimeStamp;
920        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
921
922        currentTimeStamp += firstIntervalMs;
923        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
924
925        // Now fire the first periodic scan alarm timer
926        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
927        mLooper.dispatchAll();
928
929        // Get the second periodic scan interval
930        long secondIntervalMs = mAlarmManager
931                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
932                - currentTimeStamp;
933
934        // Verify the intervals are exponential back off
935        assertEquals(firstIntervalMs * 2, secondIntervalMs);
936
937        currentTimeStamp += secondIntervalMs;
938        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
939
940        // Make sure we eventually stay at the maximum scan interval.
941        long intervalMs = 0;
942        for (int i = 0; i < 5; i++) {
943            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
944            mLooper.dispatchAll();
945            intervalMs = mAlarmManager
946                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
947                    - currentTimeStamp;
948            currentTimeStamp += intervalMs;
949            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
950        }
951
952        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
953    }
954
955    /**
956     *  Verify that scan interval for screen on and wifi connected scenario
957     *  is in the exponential backoff fashion.
958     *
959     * Expected behavior: WifiConnectivityManager doubles periodic
960     * scan interval.
961     */
962    @Test
963    public void checkPeriodicScanIntervalWhenConnected() {
964        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
965        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
966
967        // Set screen to ON
968        mWifiConnectivityManager.handleScreenStateChanged(true);
969
970        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
971        // by screen state change can settle
972        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
973        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
974
975        // Set WiFi to connected state to trigger periodic scan
976        mWifiConnectivityManager.handleConnectionStateChanged(
977                WifiConnectivityManager.WIFI_STATE_CONNECTED);
978
979        // Get the first periodic scan interval
980        long firstIntervalMs = mAlarmManager
981                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
982                - currentTimeStamp;
983        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
984
985        currentTimeStamp += firstIntervalMs;
986        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
987
988        // Now fire the first periodic scan alarm timer
989        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
990        mLooper.dispatchAll();
991
992        // Get the second periodic scan interval
993        long secondIntervalMs = mAlarmManager
994                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
995                - currentTimeStamp;
996
997        // Verify the intervals are exponential back off
998        assertEquals(firstIntervalMs * 2, secondIntervalMs);
999
1000        currentTimeStamp += secondIntervalMs;
1001        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1002
1003        // Make sure we eventually stay at the maximum scan interval.
1004        long intervalMs = 0;
1005        for (int i = 0; i < 5; i++) {
1006            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
1007            mLooper.dispatchAll();
1008            intervalMs = mAlarmManager
1009                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
1010                    - currentTimeStamp;
1011            currentTimeStamp += intervalMs;
1012            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1013        }
1014
1015        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
1016    }
1017
1018    /**
1019     *  When screen on trigger a disconnected state change event then a connected state
1020     *  change event back to back to verify that the minium scan interval is enforced.
1021     *
1022     * Expected behavior: WifiConnectivityManager start the second periodic single
1023     * scan PERIODIC_SCAN_INTERVAL_MS after the first one.
1024     */
1025    @Test
1026    public void checkMinimumPeriodicScanIntervalWhenScreenOnAndConnected() {
1027        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
1028        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1029
1030        // Set screen to ON
1031        mWifiConnectivityManager.handleScreenStateChanged(true);
1032
1033        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
1034        // by screen state change can settle
1035        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
1036        long scanForDisconnectedTimeStamp = currentTimeStamp;
1037        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1038
1039        // Set WiFi to disconnected state which triggers a scan immediately
1040        mWifiConnectivityManager.handleConnectionStateChanged(
1041                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1042        verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
1043
1044        // Set up time stamp for when entering CONNECTED state
1045        currentTimeStamp += 2000;
1046        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1047
1048        // Set WiFi to connected state to trigger its periodic scan
1049        mWifiConnectivityManager.handleConnectionStateChanged(
1050                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1051
1052        // The very first scan triggered for connected state is actually via the alarm timer
1053        // and it obeys the minimum scan interval
1054        long firstScanForConnectedTimeStamp = mAlarmManager
1055                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
1056
1057        // Verify that the first scan for connected state is scheduled PERIODIC_SCAN_INTERVAL_MS
1058        // after the scan for disconnected state
1059        assertEquals(firstScanForConnectedTimeStamp, scanForDisconnectedTimeStamp
1060                + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
1061    }
1062
1063    /**
1064     *  When screen on trigger a connected state change event then a disconnected state
1065     *  change event back to back to verify that a scan is fired immediately for the
1066     *  disconnected state change event.
1067     *
1068     * Expected behavior: WifiConnectivityManager directly starts the periodic immediately
1069     * for the disconnected state change event. The second scan for disconnected state is
1070     * via alarm timer.
1071     */
1072    @Test
1073    public void scanImmediatelyWhenScreenOnAndDisconnected() {
1074        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
1075        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1076
1077        // Set screen to ON
1078        mWifiConnectivityManager.handleScreenStateChanged(true);
1079
1080        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
1081        // by screen state change can settle
1082        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
1083        long scanForConnectedTimeStamp = currentTimeStamp;
1084        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1085
1086        // Set WiFi to connected state to trigger the periodic scan
1087        mWifiConnectivityManager.handleConnectionStateChanged(
1088                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1089        verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
1090
1091        // Set up the time stamp for when entering DISCONNECTED state
1092        currentTimeStamp += 2000;
1093        long enteringDisconnectedStateTimeStamp = currentTimeStamp;
1094        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1095
1096        // Set WiFi to disconnected state to trigger its periodic scan
1097        mWifiConnectivityManager.handleConnectionStateChanged(
1098                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1099
1100        // Verify the very first scan for DISCONNECTED state is fired immediately
1101        verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject(), anyObject());
1102        long secondScanForDisconnectedTimeStamp = mAlarmManager
1103                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
1104
1105        // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after
1106        // entering DISCONNECTED state.
1107        assertEquals(secondScanForDisconnectedTimeStamp, enteringDisconnectedStateTimeStamp
1108                + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
1109    }
1110
1111    /**
1112     *  When screen on trigger a connection state change event and a forced connectivity
1113     *  scan event back to back to verify that the minimum scan interval is not applied
1114     *  in this scenario.
1115     *
1116     * Expected behavior: WifiConnectivityManager starts the second periodic single
1117     * scan immediately.
1118     */
1119    @Test
1120    public void checkMinimumPeriodicScanIntervalNotEnforced() {
1121        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
1122        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1123
1124        // Set screen to ON
1125        mWifiConnectivityManager.handleScreenStateChanged(true);
1126
1127        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
1128        // by screen state change can settle
1129        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
1130        long firstScanTimeStamp = currentTimeStamp;
1131        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1132
1133        // Set WiFi to connected state to trigger the periodic scan
1134        mWifiConnectivityManager.handleConnectionStateChanged(
1135                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1136
1137        // Set the second scan attempt time stamp
1138        currentTimeStamp += 2000;
1139        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1140
1141        // Allow untrusted networks so WifiConnectivityManager starts a periodic scan
1142        // immediately.
1143        mWifiConnectivityManager.setUntrustedConnectionAllowed(true);
1144
1145        // Get the second periodic scan actual time stamp. Note, this scan is not
1146        // started from the AlarmManager.
1147        long secondScanTimeStamp = mWifiConnectivityManager.getLastPeriodicSingleScanTimeStamp();
1148
1149        // Verify that the second scan is fired immediately
1150        assertEquals(secondScanTimeStamp, currentTimeStamp);
1151    }
1152
1153    /**
1154     * Verify that we perform full band scan when the currently connected network's tx/rx success
1155     * rate is low.
1156     *
1157     * Expected behavior: WifiConnectivityManager does full band scan.
1158     */
1159    @Test
1160    public void checkSingleScanSettingsWhenConnectedWithLowDataRate() {
1161        mWifiInfo.txSuccessRate = 0;
1162        mWifiInfo.rxSuccessRate = 0;
1163
1164        final HashSet<Integer> channelList = new HashSet<>();
1165        channelList.add(1);
1166        channelList.add(2);
1167        channelList.add(3);
1168
1169        when(mWifiStateMachine.getCurrentWifiConfiguration())
1170                .thenReturn(new WifiConfiguration());
1171        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
1172                anyInt())).thenReturn(channelList);
1173
1174        doAnswer(new AnswerWithArguments() {
1175            public void answer(ScanSettings settings, ScanListener listener,
1176                    WorkSource workSource) throws Exception {
1177                assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
1178                assertNull(settings.channels);
1179            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1180
1181        // Set screen to ON
1182        mWifiConnectivityManager.handleScreenStateChanged(true);
1183
1184        // Set WiFi to connected state to trigger periodic scan
1185        mWifiConnectivityManager.handleConnectionStateChanged(
1186                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1187
1188        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1189    }
1190
1191    /**
1192     * Verify that we perform partial scan when the currently connected network's tx/rx success
1193     * rate is high and when the currently connected network is present in scan
1194     * cache in WifiConfigManager.
1195     * WifiConnectivityManager does partial scan only when firmware roaming is not supported.
1196     *
1197     * Expected behavior: WifiConnectivityManager does partial scan.
1198     */
1199    @Test
1200    public void checkPartialScanRequestedWithHighDataRateWithoutFwRoaming() {
1201        mWifiInfo.txSuccessRate = mFullScanMaxTxPacketRate * 2;
1202        mWifiInfo.rxSuccessRate = mFullScanMaxRxPacketRate * 2;
1203
1204        final HashSet<Integer> channelList = new HashSet<>();
1205        channelList.add(1);
1206        channelList.add(2);
1207        channelList.add(3);
1208
1209        when(mWifiStateMachine.getCurrentWifiConfiguration())
1210                .thenReturn(new WifiConfiguration());
1211        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
1212                anyInt())).thenReturn(channelList);
1213        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
1214
1215        doAnswer(new AnswerWithArguments() {
1216            public void answer(ScanSettings settings, ScanListener listener,
1217                    WorkSource workSource) throws Exception {
1218                assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED);
1219                assertEquals(settings.channels.length, channelList.size());
1220                for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) {
1221                    assertTrue(channelList.contains(settings.channels[chanIdx].frequency));
1222                }
1223            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1224
1225        // Set screen to ON
1226        mWifiConnectivityManager.handleScreenStateChanged(true);
1227
1228        // Set WiFi to connected state to trigger periodic scan
1229        mWifiConnectivityManager.handleConnectionStateChanged(
1230                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1231
1232        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1233    }
1234
1235    /**
1236     * Verify that we skip the partial scan when:
1237     * 1. The currently connected network's tx/rx success rate is high.
1238     * 2. When the currently connected network is present in scan
1239     * cache in WifiConfigManager.
1240     * 3. When firmware roaming is supported.
1241     * Expected behavior: WifiConnectivityManager does no scan, but periodic scans
1242     * are still scheduled.
1243     */
1244    @Test
1245    public void checkPartialScanSkippedWithHighDataRateWithFwRoaming() {
1246        mWifiInfo.txSuccessRate = mFullScanMaxTxPacketRate * 2;
1247        mWifiInfo.rxSuccessRate = mFullScanMaxRxPacketRate * 2;
1248
1249        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
1250        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
1251
1252        final HashSet<Integer> channelList = new HashSet<>();
1253        channelList.add(1);
1254        channelList.add(2);
1255        channelList.add(3);
1256
1257        when(mWifiStateMachine.getCurrentWifiConfiguration())
1258                .thenReturn(new WifiConfiguration());
1259        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
1260                anyInt())).thenReturn(channelList);
1261        // No scan will be requested when firmware roaming control is not supported.
1262        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1263
1264        // Set screen to ON
1265        mWifiConnectivityManager.handleScreenStateChanged(true);
1266
1267        // Set WiFi to connected state to trigger periodic scan
1268        mWifiConnectivityManager.handleConnectionStateChanged(
1269                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1270
1271        verify(mWifiScanner, never()).startScan(anyObject(), anyObject(), anyObject());
1272
1273        // Get the first periodic scan interval to check that we are still scheduling
1274        // periodic scans.
1275        long firstIntervalMs = mAlarmManager
1276                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
1277                - currentTimeStamp;
1278        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
1279    }
1280
1281    /**
1282     * Verify that we fall back to full band scan when the currently connected network's tx/rx
1283     * success rate is high and the currently connected network is not present in scan cache in
1284     * WifiConfigManager. This is simulated by returning an empty hashset in |makeChannelList|.
1285     *
1286     * Expected behavior: WifiConnectivityManager does full band scan.
1287     */
1288    @Test
1289    public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() {
1290        mWifiInfo.txSuccessRate = mFullScanMaxTxPacketRate * 2;
1291        mWifiInfo.rxSuccessRate = mFullScanMaxRxPacketRate * 2;
1292
1293        final HashSet<Integer> channelList = new HashSet<>();
1294
1295        when(mWifiStateMachine.getCurrentWifiConfiguration())
1296                .thenReturn(new WifiConfiguration());
1297        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
1298                anyInt())).thenReturn(channelList);
1299
1300        doAnswer(new AnswerWithArguments() {
1301            public void answer(ScanSettings settings, ScanListener listener,
1302                    WorkSource workSource) throws Exception {
1303                assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
1304                assertNull(settings.channels);
1305            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1306
1307        // Set screen to ON
1308        mWifiConnectivityManager.handleScreenStateChanged(true);
1309
1310        // Set WiFi to connected state to trigger periodic scan
1311        mWifiConnectivityManager.handleConnectionStateChanged(
1312                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1313
1314        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1315    }
1316
1317    /**
1318     *  Verify that we retry connectivity scan up to MAX_SCAN_RESTART_ALLOWED times
1319     *  when Wifi somehow gets into a bad state and fails to scan.
1320     *
1321     * Expected behavior: WifiConnectivityManager schedules connectivity scan
1322     * MAX_SCAN_RESTART_ALLOWED times.
1323     */
1324    @Test
1325    public void checkMaximumScanRetry() {
1326        // Set screen to ON
1327        mWifiConnectivityManager.handleScreenStateChanged(true);
1328
1329        doAnswer(new AnswerWithArguments() {
1330            public void answer(ScanSettings settings, ScanListener listener,
1331                    WorkSource workSource) throws Exception {
1332                listener.onFailure(-1, "ScanFailure");
1333            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
1334
1335        // Set WiFi to disconnected state to trigger the single scan based periodic scan
1336        mWifiConnectivityManager.handleConnectionStateChanged(
1337                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1338
1339        // Fire the alarm timer 2x timers
1340        for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) {
1341            mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG);
1342            mLooper.dispatchAll();
1343        }
1344
1345        // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED
1346        // times. Note, WifiScanner.startScan() is invoked MAX_SCAN_RESTART_ALLOWED + 1 times.
1347        // The very first scan is the initial one, and the other MAX_SCAN_RESTART_ALLOWED
1348        // are the retrial ones.
1349        verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 1)).startScan(
1350                anyObject(), anyObject(), anyObject());
1351    }
1352
1353    /**
1354     * Listen to scan results not requested by WifiConnectivityManager and
1355     * act on them.
1356     *
1357     * Expected behavior: WifiConnectivityManager calls
1358     * WifiStateMachine.startConnectToNetwork() with the
1359     * expected candidate network ID and BSSID.
1360     */
1361    @Test
1362    public void listenToAllSingleScanResults() {
1363        ScanSettings settings = new ScanSettings();
1364        ScanListener scanListener = mock(ScanListener.class);
1365
1366        // Request a single scan outside of WifiConnectivityManager.
1367        mWifiScanner.startScan(settings, scanListener, WIFI_WORK_SOURCE);
1368
1369        // Verify that WCM receives the scan results and initiates a connection
1370        // to the network.
1371        verify(mWifiStateMachine).startConnectToNetwork(
1372                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1373    }
1374
1375    /**
1376     *  Verify that a forced connectivity scan waits for full band scan
1377     *  results.
1378     *
1379     * Expected behavior: WifiConnectivityManager doesn't invoke
1380     * WifiStateMachine.startConnectToNetwork() when full band scan
1381     * results are not available.
1382     */
1383    @Test
1384    public void waitForFullBandScanResults() {
1385        // Set WiFi to connected state.
1386        mWifiConnectivityManager.handleConnectionStateChanged(
1387                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1388
1389        // Set up as partial scan results.
1390        when(mScanData.isAllChannelsScanned()).thenReturn(false);
1391
1392        // Force a connectivity scan which enables WifiConnectivityManager
1393        // to wait for full band scan results.
1394        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
1395
1396        // No roaming because no full band scan results.
1397        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
1398                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1399
1400        // Set up as full band scan results.
1401        when(mScanData.isAllChannelsScanned()).thenReturn(true);
1402
1403        // Force a connectivity scan which enables WifiConnectivityManager
1404        // to wait for full band scan results.
1405        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
1406
1407        // Roaming attempt because full band scan results are available.
1408        verify(mWifiStateMachine).startConnectToNetwork(
1409                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1410    }
1411
1412    /**
1413     *  Verify the BSSID blacklist implementation.
1414     *
1415     * Expected behavior: A BSSID gets blacklisted after being disabled
1416     * for 3 times, and becomes available after being re-enabled. Firmware
1417     * controlled roaming is supported, its roaming configuration needs to be
1418     * updated as well.
1419     */
1420    @Test
1421    public void blacklistAndReenableBssid() {
1422        String bssid = "6c:f3:7f:ae:8c:f3";
1423
1424        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1425        // Verify that a BSSID gets blacklisted only after being disabled
1426        // for BSSID_BLACKLIST_THRESHOLD times for reasons other than
1427        // REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA.
1428        for (int i = 0; i < WifiConnectivityManager.BSSID_BLACKLIST_THRESHOLD; i++) {
1429            assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1430            mWifiConnectivityManager.trackBssid(bssid, false, 1);
1431        }
1432
1433        // Verify the BSSID is now blacklisted.
1434        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1435        // Verify the BSSID gets sent to firmware.
1436        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1437                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1438        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1439        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1440
1441        // Re-enable the bssid.
1442        mWifiConnectivityManager.trackBssid(bssid, true, 1);
1443
1444        // Verify the bssid is no longer blacklisted.
1445        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1446        // Verify the BSSID gets cleared from firmware.
1447        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1448                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1449        assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
1450        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1451    }
1452
1453    /**
1454     *  Verify that a network gets blacklisted immediately if it is unable
1455     *  to handle new stations.
1456     */
1457    @Test
1458    public void blacklistNetworkImmediatelyIfApHasNoCapacityForNewStation() {
1459        String bssid = "6c:f3:7f:ae:8c:f3";
1460
1461        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1462        // Blacklist the BSSID
1463        mWifiConnectivityManager.trackBssid(bssid, false,
1464                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1465
1466        // Verify the BSSID is now blacklisted.
1467        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1468        // Verify the BSSID gets sent to firmware.
1469        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1470                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1471        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1472        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1473    }
1474
1475    /**
1476     *  Verify that a blacklisted BSSID becomes available only after
1477     *  BSSID_BLACKLIST_EXPIRE_TIME_MS.
1478     */
1479    @Test
1480    public void verifyBlacklistRefreshedAfterScanResults() {
1481        String bssid = "6c:f3:7f:ae:8c:f3";
1482
1483        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1484        // Blacklist the BSSID.
1485        mWifiConnectivityManager.trackBssid(bssid, false,
1486                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1487
1488        // Verify the BSSID is now blacklisted.
1489        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1490        // Verify the BSSID gets sent to firmware.
1491        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1492                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1493        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1494        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1495
1496        // Force a connectivity scan in less than BSSID_BLACKLIST_EXPIRE_TIME_MS.
1497        // Arrival of scan results will trigger WifiConnectivityManager to refresh its
1498        // BSSID blacklist. Verify that the blacklisted BSSId is not freed because
1499        // its blacklist expiration time hasn't reached yet.
1500        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
1501                + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS / 2);
1502        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
1503        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1504
1505        // Force another connectivity scan at BSSID_BLACKLIST_EXPIRE_TIME_MS from when the
1506        // BSSID was blacklisted. Verify that the blacklisted BSSId is freed.
1507        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
1508                + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS);
1509        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
1510
1511        // Verify the BSSID is no longer blacklisted.
1512        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1513        // Verify the BSSID gets cleared from firmware.
1514        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1515                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1516        assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
1517        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1518    }
1519
1520    /**
1521     *  Verify that BSSID blacklist gets cleared when exiting Wifi client mode.
1522     */
1523    @Test
1524    public void clearBssidBlacklistWhenExitingWifiClientMode() {
1525        String bssid = "6c:f3:7f:ae:8c:f3";
1526
1527        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1528
1529        // Blacklist the BSSID.
1530        mWifiConnectivityManager.trackBssid(bssid, false,
1531                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1532
1533        // Verify the BSSID is now blacklisted.
1534        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1535        // Verify the BSSID gets sent to firmware.
1536        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1537                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1538        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1539        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1540
1541        // Exit Wifi client mode.
1542        mWifiConnectivityManager.setWifiEnabled(false);
1543
1544        // Verify the BSSID blacklist is empty.
1545        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1546        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1547                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1548        assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
1549        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1550    }
1551
1552    /**
1553     *  Verify that BSSID blacklist gets cleared when preparing for a forced connection
1554     *  initiated by user/app.
1555     */
1556    @Test
1557    public void clearBssidBlacklistWhenPreparingForForcedConnection() {
1558        String bssid = "6c:f3:7f:ae:8c:f3";
1559
1560        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1561
1562        // Blacklist the BSSID.
1563        mWifiConnectivityManager.trackBssid(bssid, false,
1564                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1565
1566        // Verify the BSSID is now blacklisted.
1567        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1568        // Verify the BSSID gets sent to firmware.
1569        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1570                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1571        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1572        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1573
1574        // Prepare for a forced connection attempt.
1575        mWifiConnectivityManager.prepareForForcedConnection(1);
1576
1577        // Verify the BSSID blacklist is empty.
1578        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1579        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1580                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1581        assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
1582        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1583    }
1584
1585    /**
1586    /**
1587     *  Verify that BSSID blacklist gets trimmed down to fit firmware capability.
1588     */
1589    @Test
1590    public void trimDownBssidBlacklistForFirmware() {
1591        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1592
1593        // Blacklist more than MAX_BSSID_BLACKLIST_SIZE BSSIDs.
1594        for (int i = 0; i < MAX_BSSID_BLACKLIST_SIZE + 6; i++) {
1595            StringBuilder bssid = new StringBuilder("55:44:33:22:11:00");
1596            bssid.setCharAt(16, (char) ('0' + i));
1597            mWifiConnectivityManager.trackBssid(bssid.toString(), false,
1598                    WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1599            // Verify that up to MAX_BSSID_BLACKLIST_SIZE BSSIDs gets sent to firmware.
1600            verify(mWifiConnectivityHelper, times(i + 1)).setFirmwareRoamingConfiguration(
1601                    mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1602            assertEquals((i + 1) <  MAX_BSSID_BLACKLIST_SIZE ? (i + 1) : MAX_BSSID_BLACKLIST_SIZE,
1603                    mBssidBlacklistCaptor.getValue().size());
1604            assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1605        }
1606    }
1607
1608    /**
1609     * When WifiConnectivityManager is on and Wifi client mode is enabled, framework
1610     * queries firmware via WifiConnectivityHelper to check if firmware roaming is
1611     * supported and its capability.
1612     *
1613     * Expected behavior: WifiConnectivityManager#setWifiEnabled calls into
1614     * WifiConnectivityHelper#getFirmwareRoamingInfo
1615     */
1616    @Test
1617    public void verifyGetFirmwareRoamingInfoIsCalledWhenEnableWiFiAndWcmOn() {
1618        reset(mWifiConnectivityHelper);
1619        // WifiConnectivityManager is on by default
1620        mWifiConnectivityManager.setWifiEnabled(true);
1621        verify(mWifiConnectivityHelper).getFirmwareRoamingInfo();
1622    }
1623
1624    /**
1625     * When WifiConnectivityManager is off,  verify that framework does not
1626     * query firmware via WifiConnectivityHelper to check if firmware roaming is
1627     * supported and its capability when enabling Wifi client mode.
1628     *
1629     * Expected behavior: WifiConnectivityManager#setWifiEnabled does not call into
1630     * WifiConnectivityHelper#getFirmwareRoamingInfo
1631     */
1632    @Test
1633    public void verifyGetFirmwareRoamingInfoIsNotCalledWhenEnableWiFiAndWcmOff() {
1634        reset(mWifiConnectivityHelper);
1635        mWifiConnectivityManager.enable(false);
1636        mWifiConnectivityManager.setWifiEnabled(true);
1637        verify(mWifiConnectivityHelper, times(0)).getFirmwareRoamingInfo();
1638    }
1639
1640    /*
1641     * Firmware supports controlled roaming.
1642     * Connect to a network which doesn't have a config specified BSSID.
1643     *
1644     * Expected behavior: WifiConnectivityManager calls
1645     * WifiStateMachine.startConnectToNetwork() with the
1646     * expected candidate network ID, and the BSSID value should be
1647     * 'any' since firmware controls the roaming.
1648     */
1649    @Test
1650    public void useAnyBssidToConnectWhenFirmwareRoamingOnAndConfigHasNoBssidSpecified() {
1651        // Firmware controls roaming
1652        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1653
1654        // Set screen to on
1655        mWifiConnectivityManager.handleScreenStateChanged(true);
1656
1657        // Set WiFi to disconnected state
1658        mWifiConnectivityManager.handleConnectionStateChanged(
1659                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1660
1661        verify(mWifiStateMachine).startConnectToNetwork(
1662                CANDIDATE_NETWORK_ID, Process.WIFI_UID, WifiStateMachine.SUPPLICANT_BSSID_ANY);
1663    }
1664
1665    /*
1666     * Firmware supports controlled roaming.
1667     * Connect to a network which has a config specified BSSID.
1668     *
1669     * Expected behavior: WifiConnectivityManager calls
1670     * WifiStateMachine.startConnectToNetwork() with the
1671     * expected candidate network ID, and the BSSID value should be
1672     * the config specified one.
1673     */
1674    @Test
1675    public void useConfigSpecifiedBssidToConnectWhenFirmwareRoamingOn() {
1676        // Firmware controls roaming
1677        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1678
1679        // Set up the candidate configuration such that it has a BSSID specified.
1680        WifiConfiguration candidate = generateWifiConfig(
1681                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1682        candidate.BSSID = CANDIDATE_BSSID; // config specified
1683        ScanResult candidateScanResult = new ScanResult();
1684        candidateScanResult.SSID = CANDIDATE_SSID;
1685        candidateScanResult.BSSID = CANDIDATE_BSSID;
1686        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1687
1688        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1689                anyBoolean(), anyBoolean())).thenReturn(candidate);
1690
1691        // Set screen to on
1692        mWifiConnectivityManager.handleScreenStateChanged(true);
1693
1694        // Set WiFi to disconnected state
1695        mWifiConnectivityManager.handleConnectionStateChanged(
1696                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1697
1698        verify(mWifiStateMachine).startConnectToNetwork(
1699                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1700    }
1701
1702    /*
1703     * Firmware does not support controlled roaming.
1704     * Connect to a network which doesn't have a config specified BSSID.
1705     *
1706     * Expected behavior: WifiConnectivityManager calls
1707     * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
1708     * and the BSSID value should be the candidate scan result specified.
1709     */
1710    @Test
1711    public void useScanResultBssidToConnectWhenFirmwareRoamingOffAndConfigHasNoBssidSpecified() {
1712        // Set screen to on
1713        mWifiConnectivityManager.handleScreenStateChanged(true);
1714
1715        // Set WiFi to disconnected state
1716        mWifiConnectivityManager.handleConnectionStateChanged(
1717                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1718
1719        verify(mWifiStateMachine).startConnectToNetwork(
1720                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1721    }
1722
1723    /*
1724     * Firmware does not support controlled roaming.
1725     * Connect to a network which has a config specified BSSID.
1726     *
1727     * Expected behavior: WifiConnectivityManager calls
1728     * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
1729     * and the BSSID value should be the config specified one.
1730     */
1731    @Test
1732    public void useConfigSpecifiedBssidToConnectionWhenFirmwareRoamingOff() {
1733        // Set up the candidate configuration such that it has a BSSID specified.
1734        WifiConfiguration candidate = generateWifiConfig(
1735                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1736        candidate.BSSID = CANDIDATE_BSSID; // config specified
1737        ScanResult candidateScanResult = new ScanResult();
1738        candidateScanResult.SSID = CANDIDATE_SSID;
1739        candidateScanResult.BSSID = CANDIDATE_BSSID;
1740        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1741
1742        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1743                anyBoolean(), anyBoolean())).thenReturn(candidate);
1744
1745        // Set screen to on
1746        mWifiConnectivityManager.handleScreenStateChanged(true);
1747
1748        // Set WiFi to disconnected state
1749        mWifiConnectivityManager.handleConnectionStateChanged(
1750                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1751
1752        verify(mWifiStateMachine).startConnectToNetwork(
1753                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1754    }
1755
1756    /**
1757     * Firmware does not support controlled roaming.
1758     * WiFi in connected state, framework triggers roaming.
1759     *
1760     * Expected behavior: WifiConnectivityManager invokes
1761     * WifiStateMachine.startRoamToNetwork().
1762     */
1763    @Test
1764    public void frameworkInitiatedRoaming() {
1765        // Mock the currently connected network which has the same networkID and
1766        // SSID as the one to be selected.
1767        WifiConfiguration currentNetwork = generateWifiConfig(
1768                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1769        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1770
1771        // Set WiFi to connected state
1772        mWifiConnectivityManager.handleConnectionStateChanged(
1773                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1774
1775        // Set screen to on
1776        mWifiConnectivityManager.handleScreenStateChanged(true);
1777
1778        verify(mWifiStateMachine).startRoamToNetwork(eq(CANDIDATE_NETWORK_ID),
1779                mCandidateScanResultCaptor.capture());
1780        assertEquals(mCandidateScanResultCaptor.getValue().BSSID, CANDIDATE_BSSID);
1781    }
1782
1783    /**
1784     * Firmware supports controlled roaming.
1785     * WiFi in connected state, framework does not trigger roaming
1786     * as it's handed off to the firmware.
1787     *
1788     * Expected behavior: WifiConnectivityManager doesn't invoke
1789     * WifiStateMachine.startRoamToNetwork().
1790     */
1791    @Test
1792    public void noFrameworkRoamingIfConnectedAndFirmwareRoamingSupported() {
1793        // Mock the currently connected network which has the same networkID and
1794        // SSID as the one to be selected.
1795        WifiConfiguration currentNetwork = generateWifiConfig(
1796                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1797        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1798
1799        // Firmware controls roaming
1800        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1801
1802        // Set WiFi to connected state
1803        mWifiConnectivityManager.handleConnectionStateChanged(
1804                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1805
1806        // Set screen to on
1807        mWifiConnectivityManager.handleScreenStateChanged(true);
1808
1809        verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
1810    }
1811
1812    /*
1813     * Wifi in disconnected state. Drop the connection attempt if the recommended
1814     * network configuration has a BSSID specified but the scan result BSSID doesn't
1815     * match it.
1816     *
1817     * Expected behavior: WifiConnectivityManager doesn't invoke
1818     * WifiStateMachine.startConnectToNetwork().
1819     */
1820    @Test
1821    public void dropConnectAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
1822        // Set up the candidate configuration such that it has a BSSID specified.
1823        WifiConfiguration candidate = generateWifiConfig(
1824                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1825        candidate.BSSID = CANDIDATE_BSSID; // config specified
1826        ScanResult candidateScanResult = new ScanResult();
1827        candidateScanResult.SSID = CANDIDATE_SSID;
1828        // Set up the scan result BSSID to be different from the config specified one.
1829        candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
1830        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1831
1832        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1833                anyBoolean(), anyBoolean())).thenReturn(candidate);
1834
1835        // Set screen to on
1836        mWifiConnectivityManager.handleScreenStateChanged(true);
1837
1838        // Set WiFi to disconnected state
1839        mWifiConnectivityManager.handleConnectionStateChanged(
1840                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1841
1842        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
1843                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
1844    }
1845
1846    /*
1847     * Wifi in connected state. Drop the roaming attempt if the recommended
1848     * network configuration has a BSSID specified but the scan result BSSID doesn't
1849     * match it.
1850     *
1851     * Expected behavior: WifiConnectivityManager doesn't invoke
1852     * WifiStateMachine.startRoamToNetwork().
1853     */
1854    @Test
1855    public void dropRoamingAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
1856        // Mock the currently connected network which has the same networkID and
1857        // SSID as the one to be selected.
1858        WifiConfiguration currentNetwork = generateWifiConfig(
1859                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1860        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1861
1862        // Set up the candidate configuration such that it has a BSSID specified.
1863        WifiConfiguration candidate = generateWifiConfig(
1864                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1865        candidate.BSSID = CANDIDATE_BSSID; // config specified
1866        ScanResult candidateScanResult = new ScanResult();
1867        candidateScanResult.SSID = CANDIDATE_SSID;
1868        // Set up the scan result BSSID to be different from the config specified one.
1869        candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
1870        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1871
1872        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1873                anyBoolean(), anyBoolean())).thenReturn(candidate);
1874
1875        // Set WiFi to connected state
1876        mWifiConnectivityManager.handleConnectionStateChanged(
1877                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1878
1879        // Set screen to on
1880        mWifiConnectivityManager.handleScreenStateChanged(true);
1881
1882        verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
1883    }
1884
1885    /**
1886     *  Dump local log buffer.
1887     *
1888     * Expected behavior: Logs dumped from WifiConnectivityManager.dump()
1889     * contain the message we put in mLocalLog.
1890     */
1891    @Test
1892    public void dumpLocalLog() {
1893        final String localLogMessage = "This is a message from the test";
1894        mLocalLog.log(localLogMessage);
1895
1896        StringWriter sw = new StringWriter();
1897        PrintWriter pw = new PrintWriter(sw);
1898        mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
1899        assertTrue(sw.toString().contains(localLogMessage));
1900    }
1901
1902    /**
1903     *  Dump ONA controller.
1904     *
1905     * Expected behavior: {@link OpenNetworkNotifier#dump(FileDescriptor, PrintWriter,
1906     * String[])} is invoked.
1907     */
1908    @Test
1909    public void dumpNotificationController() {
1910        StringWriter sw = new StringWriter();
1911        PrintWriter pw = new PrintWriter(sw);
1912        mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
1913
1914        verify(mOpenNetworkNotifier).dump(any(), any(), any());
1915    }
1916
1917    /**
1918     * Create scan data with different radio chain infos:
1919     * First scan result has null radio chain info (No DBS support).
1920     * Second scan result has empty radio chain info (No DBS support).
1921     * Third scan result has 1 radio chain info (DBS scan).
1922     * Fourth scan result has 2 radio chain info (non-DBS scan).
1923     */
1924    private ScanData createScanDataWithDifferentRadioChainInfos() {
1925        // Create 4 scan results.
1926        ScanData[] scanDatas =
1927                ScanTestUtil.createScanDatas(new int[][]{{5150, 5175, 2412, 2400}}, new int[]{0});
1928        // WCM barfs if the scan result does not have an IE.
1929        scanDatas[0].getResults()[0].informationElements = new InformationElement[0];
1930        scanDatas[0].getResults()[1].informationElements = new InformationElement[0];
1931        scanDatas[0].getResults()[2].informationElements = new InformationElement[0];
1932        scanDatas[0].getResults()[3].informationElements = new InformationElement[0];
1933        scanDatas[0].getResults()[0].radioChainInfos = null;
1934        scanDatas[0].getResults()[1].radioChainInfos = new ScanResult.RadioChainInfo[0];
1935        scanDatas[0].getResults()[2].radioChainInfos = new ScanResult.RadioChainInfo[1];
1936        scanDatas[0].getResults()[3].radioChainInfos = new ScanResult.RadioChainInfo[2];
1937
1938        return scanDatas[0];
1939    }
1940
1941    /**
1942     * If |config_wifi_framework_use_single_radio_chain_scan_results_network_selection| flag is
1943     * false, WifiConnectivityManager should filter scan results which contain scans from a single
1944     * radio chain (i.e DBS scan).
1945     * Note:
1946     * a) ScanResult with no radio chain indicates a lack of DBS support on the device.
1947     * b) ScanResult with 2 radio chain info indicates a scan done using both the radio chains
1948     * on a DBS supported device.
1949     *
1950     * Expected behavior: WifiConnectivityManager invokes
1951     * {@link WifiNetworkSelector#selectNetwork(List, HashSet, WifiInfo, boolean, boolean, boolean)}
1952     * after filtering out the scan results obtained via DBS scan.
1953     */
1954    @Test
1955    public void filterScanResultsWithOneRadioChainInfoForNetworkSelectionIfConfigDisabled() {
1956        when(mResource.getBoolean(
1957                R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection))
1958                .thenReturn(false);
1959        when(mWifiNS.selectNetwork(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()))
1960                .thenReturn(null);
1961        mWifiConnectivityManager = createConnectivityManager();
1962
1963        mScanData = createScanDataWithDifferentRadioChainInfos();
1964
1965        // Capture scan details which were sent to network selector.
1966        final List<ScanDetail> capturedScanDetails = new ArrayList<>();
1967        doAnswer(new AnswerWithArguments() {
1968            public WifiConfiguration answer(
1969                    List<ScanDetail> scanDetails, HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
1970                    boolean connected, boolean disconnected, boolean untrustedNetworkAllowed)
1971                    throws Exception {
1972                capturedScanDetails.addAll(scanDetails);
1973                return null;
1974            }}).when(mWifiNS).selectNetwork(
1975                    any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean());
1976
1977        // Set WiFi to disconnected state with screen on which triggers a scan immediately.
1978        mWifiConnectivityManager.setWifiEnabled(true);
1979        mWifiConnectivityManager.handleScreenStateChanged(true);
1980        mWifiConnectivityManager.handleConnectionStateChanged(
1981                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1982
1983        // We should have filtered out the 3rd scan result.
1984        assertEquals(3, capturedScanDetails.size());
1985        List<ScanResult> capturedScanResults =
1986                capturedScanDetails.stream().map(ScanDetail::getScanResult)
1987                        .collect(Collectors.toList());
1988
1989        assertEquals(3, capturedScanResults.size());
1990        assertTrue(capturedScanResults.contains(mScanData.getResults()[0]));
1991        assertTrue(capturedScanResults.contains(mScanData.getResults()[1]));
1992        assertFalse(capturedScanResults.contains(mScanData.getResults()[2]));
1993        assertTrue(capturedScanResults.contains(mScanData.getResults()[3]));
1994    }
1995
1996    /**
1997     * If |config_wifi_framework_use_single_radio_chain_scan_results_network_selection| flag is
1998     * true, WifiConnectivityManager should not filter scan results which contain scans from a
1999     * single radio chain (i.e DBS scan).
2000     * Note:
2001     * a) ScanResult with no radio chain indicates a lack of DBS support on the device.
2002     * b) ScanResult with 2 radio chain info indicates a scan done using both the radio chains
2003     * on a DBS supported device.
2004     *
2005     * Expected behavior: WifiConnectivityManager invokes
2006     * {@link WifiNetworkSelector#selectNetwork(List, HashSet, WifiInfo, boolean, boolean, boolean)}
2007     * after filtering out the scan results obtained via DBS scan.
2008     */
2009    @Test
2010    public void dontFilterScanResultsWithOneRadioChainInfoForNetworkSelectionIfConfigEnabled() {
2011        when(mResource.getBoolean(
2012                R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection))
2013                .thenReturn(true);
2014        when(mWifiNS.selectNetwork(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()))
2015                .thenReturn(null);
2016        mWifiConnectivityManager = createConnectivityManager();
2017
2018        mScanData = createScanDataWithDifferentRadioChainInfos();
2019
2020        // Capture scan details which were sent to network selector.
2021        final List<ScanDetail> capturedScanDetails = new ArrayList<>();
2022        doAnswer(new AnswerWithArguments() {
2023            public WifiConfiguration answer(
2024                    List<ScanDetail> scanDetails, HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
2025                    boolean connected, boolean disconnected, boolean untrustedNetworkAllowed)
2026                    throws Exception {
2027                capturedScanDetails.addAll(scanDetails);
2028                return null;
2029            }}).when(mWifiNS).selectNetwork(
2030                any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean());
2031
2032        // Set WiFi to disconnected state with screen on which triggers a scan immediately.
2033        mWifiConnectivityManager.setWifiEnabled(true);
2034        mWifiConnectivityManager.handleScreenStateChanged(true);
2035        mWifiConnectivityManager.handleConnectionStateChanged(
2036                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
2037
2038        // We should not filter any of the scan results.
2039        assertEquals(4, capturedScanDetails.size());
2040        List<ScanResult> capturedScanResults =
2041                capturedScanDetails.stream().map(ScanDetail::getScanResult)
2042                        .collect(Collectors.toList());
2043
2044        assertEquals(4, capturedScanResults.size());
2045        assertTrue(capturedScanResults.contains(mScanData.getResults()[0]));
2046        assertTrue(capturedScanResults.contains(mScanData.getResults()[1]));
2047        assertTrue(capturedScanResults.contains(mScanData.getResults()[2]));
2048        assertTrue(capturedScanResults.contains(mScanData.getResults()[3]));
2049    }
2050
2051    /**
2052     * Disabling the network temporarily due to lack of internet is a special reason for which we
2053     * don't want WCM to trigger a disconnect (by removing the network from supplicant).
2054     */
2055    @Test
2056    public void dontDisconnectIfNetworkTemporarilyDisabledDueToNoInternet() {
2057        assertNotNull(mSavedNetworkUpdateListenerCaptor.getValue());
2058
2059        mSavedNetworkUpdateListenerCaptor.getValue()
2060                .onSavedNetworkPermanentlyDisabled(0, DISABLED_AUTHENTICATION_FAILURE);
2061        verify(mWifiConnectivityHelper).removeNetworkIfCurrent(0);
2062
2063        mSavedNetworkUpdateListenerCaptor.getValue()
2064                .onSavedNetworkPermanentlyDisabled(0, DISABLED_NO_INTERNET_TEMPORARY);
2065        // Don't remove network.
2066    }
2067}
2068