1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
20import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE;
21
22import static org.junit.Assert.*;
23import static org.mockito.Mockito.*;
24
25import android.app.test.MockAnswerUtil.AnswerWithArguments;
26import android.app.test.TestAlarmManager;
27import android.content.Context;
28import android.content.res.Resources;
29import android.net.NetworkScoreManager;
30import android.net.wifi.ScanResult;
31import android.net.wifi.ScanResult.InformationElement;
32import android.net.wifi.SupplicantState;
33import android.net.wifi.WifiConfiguration;
34import android.net.wifi.WifiInfo;
35import android.net.wifi.WifiNetworkScoreCache;
36import android.net.wifi.WifiScanner;
37import android.net.wifi.WifiScanner.PnoScanListener;
38import android.net.wifi.WifiScanner.PnoSettings;
39import android.net.wifi.WifiScanner.ScanData;
40import android.net.wifi.WifiScanner.ScanListener;
41import android.net.wifi.WifiScanner.ScanSettings;
42import android.net.wifi.WifiSsid;
43import android.os.SystemClock;
44import android.os.WorkSource;
45import android.os.test.TestLooper;
46import android.test.suitebuilder.annotation.SmallTest;
47import android.util.LocalLog;
48
49import com.android.internal.R;
50
51import org.junit.After;
52import org.junit.Before;
53import org.junit.Ignore;
54import org.junit.Test;
55import org.mockito.ArgumentCaptor;
56import org.mockito.Captor;
57import org.mockito.Mock;
58import org.mockito.MockitoAnnotations;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.io.StringWriter;
63import java.nio.charset.StandardCharsets;
64import java.util.ArrayList;
65import java.util.HashSet;
66
67/**
68 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
69 */
70@SmallTest
71public class WifiConnectivityManagerTest {
72    /**
73     * Called before each test
74     */
75    @Before
76    public void setUp() throws Exception {
77        MockitoAnnotations.initMocks(this);
78        mResource = mockResource();
79        mAlarmManager = new TestAlarmManager();
80        mContext = mockContext();
81        mLocalLog = new LocalLog(512);
82        mWifiStateMachine = mockWifiStateMachine();
83        mWifiConfigManager = mockWifiConfigManager();
84        mWifiInfo = getWifiInfo();
85        mScanData = mockScanData();
86        mWifiScanner = mockWifiScanner();
87        mWifiConnectivityHelper = mockWifiConnectivityHelper();
88        mWifiNS = mockWifiNetworkSelector();
89        mWifiConnectivityManager = createConnectivityManager();
90        verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject());
91        mWifiConnectivityManager.setWifiEnabled(true);
92        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
93    }
94
95    /**
96     * Called after each test
97     */
98    @After
99    public void cleanup() {
100        validateMockitoUsage();
101    }
102
103    private Resources mResource;
104
105    private Context mContext;
106    private TestAlarmManager mAlarmManager;
107    private TestLooper mLooper = new TestLooper();
108    private WifiConnectivityManager mWifiConnectivityManager;
109    private WifiNetworkSelector mWifiNS;
110    private WifiStateMachine mWifiStateMachine;
111    private WifiScanner mWifiScanner;
112    private WifiConnectivityHelper mWifiConnectivityHelper;
113    private ScanData mScanData;
114    private WifiConfigManager mWifiConfigManager;
115    private WifiInfo mWifiInfo;
116    private LocalLog mLocalLog;
117    @Mock private FrameworkFacade mFrameworkFacade;
118    @Mock private NetworkScoreManager mNetworkScoreManager;
119    @Mock private Clock mClock;
120    @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog;
121    @Mock private WifiMetrics mWifiMetrics;
122    @Mock private WifiNetworkScoreCache mScoreCache;
123    @Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor;
124    @Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor;
125    @Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor;
126    private MockResources mResources;
127
128    private static final int CANDIDATE_NETWORK_ID = 0;
129    private static final String CANDIDATE_SSID = "\"AnSsid\"";
130    private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3";
131    private static final String INVALID_SCAN_RESULT_BSSID = "6c:f3:7f:ae:8c:f4";
132    private static final long CURRENT_SYSTEM_TIME_MS = 1000;
133    private static final int MAX_BSSID_BLACKLIST_SIZE = 16;
134
135
136    Resources mockResource() {
137        Resources resource = mock(Resources.class);
138
139        when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
140        when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
141        when(resource.getBoolean(
142                R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true);
143        when(resource.getInteger(
144                R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
145                .thenReturn(-60);
146        when(resource.getInteger(
147                R.integer.config_wifi_framework_current_network_boost)).thenReturn(16);
148        return resource;
149    }
150
151    Context mockContext() {
152        Context context = mock(Context.class);
153
154        when(context.getResources()).thenReturn(mResource);
155        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
156                mAlarmManager.getAlarmManager());
157
158        return context;
159    }
160
161    ScanData mockScanData() {
162        ScanData scanData = mock(ScanData.class);
163
164        when(scanData.isAllChannelsScanned()).thenReturn(true);
165
166        return scanData;
167    }
168
169    WifiScanner mockWifiScanner() {
170        WifiScanner scanner = mock(WifiScanner.class);
171        ArgumentCaptor<ScanListener> allSingleScanListenerCaptor =
172                ArgumentCaptor.forClass(ScanListener.class);
173
174        doNothing().when(scanner).registerScanListener(allSingleScanListenerCaptor.capture());
175
176        ScanData[] scanDatas = new ScanData[1];
177        scanDatas[0] = mScanData;
178
179        // do a synchronous answer for the ScanListener callbacks
180        doAnswer(new AnswerWithArguments() {
181            public void answer(ScanSettings settings, ScanListener listener,
182                    WorkSource workSource) throws Exception {
183                listener.onResults(scanDatas);
184            }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
185
186        doAnswer(new AnswerWithArguments() {
187            public void answer(ScanSettings settings, ScanListener listener,
188                    WorkSource workSource) throws Exception {
189                listener.onResults(scanDatas);
190                allSingleScanListenerCaptor.getValue().onResults(scanDatas);
191            }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
192
193        // This unfortunately needs to be a somewhat valid scan result, otherwise
194        // |ScanDetailUtil.toScanDetail| raises exceptions.
195        final ScanResult[] scanResults = new ScanResult[1];
196        scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
197                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps",
198                -78, 2450, 1025, 22, 33, 20, 0, 0, true);
199        scanResults[0].informationElements = new InformationElement[1];
200        scanResults[0].informationElements[0] = new InformationElement();
201        scanResults[0].informationElements[0].id = InformationElement.EID_SSID;
202        scanResults[0].informationElements[0].bytes =
203            CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
204
205        doAnswer(new AnswerWithArguments() {
206            public void answer(ScanSettings settings, PnoSettings pnoSettings,
207                    PnoScanListener listener) throws Exception {
208                listener.onPnoNetworkFound(scanResults);
209            }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject());
210
211        doAnswer(new AnswerWithArguments() {
212            public void answer(ScanSettings settings, PnoSettings pnoSettings,
213                    PnoScanListener listener) throws Exception {
214                listener.onPnoNetworkFound(scanResults);
215            }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject());
216
217        return scanner;
218    }
219
220    WifiConnectivityHelper mockWifiConnectivityHelper() {
221        WifiConnectivityHelper connectivityHelper = mock(WifiConnectivityHelper.class);
222
223        when(connectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
224        when(connectivityHelper.getMaxNumBlacklistBssid()).thenReturn(MAX_BSSID_BLACKLIST_SIZE);
225
226        return connectivityHelper;
227    }
228
229    WifiStateMachine mockWifiStateMachine() {
230        WifiStateMachine stateMachine = mock(WifiStateMachine.class);
231
232        when(stateMachine.isLinkDebouncing()).thenReturn(false);
233        when(stateMachine.isConnected()).thenReturn(false);
234        when(stateMachine.isDisconnected()).thenReturn(true);
235        when(stateMachine.isSupplicantTransientState()).thenReturn(false);
236
237        return stateMachine;
238    }
239
240    WifiNetworkSelector mockWifiNetworkSelector() {
241        WifiNetworkSelector ns = mock(WifiNetworkSelector.class);
242
243        WifiConfiguration candidate = generateWifiConfig(
244                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
245        candidate.BSSID = WifiStateMachine.SUPPLICANT_BSSID_ANY;
246        ScanResult candidateScanResult = new ScanResult();
247        candidateScanResult.SSID = CANDIDATE_SSID;
248        candidateScanResult.BSSID = CANDIDATE_BSSID;
249        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
250
251        when(ns.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
252                anyBoolean(), anyBoolean())).thenReturn(candidate);
253        return ns;
254    }
255
256    WifiInfo getWifiInfo() {
257        WifiInfo wifiInfo = new WifiInfo();
258
259        wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
260        wifiInfo.setBSSID(null);
261        wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
262
263        return wifiInfo;
264    }
265
266    WifiConfigManager mockWifiConfigManager() {
267        WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
268
269        when(wifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null);
270
271        // Pass dummy pno network list, otherwise Pno scan requests will not be triggered.
272        PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID);
273        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>();
274        pnoNetworkList.add(pnoNetwork);
275        when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
276        when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
277
278        return wifiConfigManager;
279    }
280
281    WifiConnectivityManager createConnectivityManager() {
282        return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner,
283                mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper,
284                mWifiLastResortWatchdog, mWifiMetrics, mLooper.getLooper(), mClock,
285                mLocalLog, true, mFrameworkFacade, null, null, null);
286    }
287
288    /**
289     *  Wifi enters disconnected state while screen is on.
290     *
291     * Expected behavior: WifiConnectivityManager calls
292     * WifiStateMachine.startConnectToNetwork() with the
293     * expected candidate network ID and BSSID.
294     */
295    @Test
296    public void enterWifiDisconnectedStateWhenScreenOn() {
297        // Set screen to on
298        mWifiConnectivityManager.handleScreenStateChanged(true);
299
300        // Set WiFi to disconnected state
301        mWifiConnectivityManager.handleConnectionStateChanged(
302                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
303
304        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
305    }
306
307    /**
308     *  Wifi enters connected state while screen is on.
309     *
310     * Expected behavior: WifiConnectivityManager calls
311     * WifiStateMachine.startConnectToNetwork() with the
312     * expected candidate network ID and BSSID.
313     */
314    @Test
315    public void enterWifiConnectedStateWhenScreenOn() {
316        // Set screen to on
317        mWifiConnectivityManager.handleScreenStateChanged(true);
318
319        // Set WiFi to connected state
320        mWifiConnectivityManager.handleConnectionStateChanged(
321                WifiConnectivityManager.WIFI_STATE_CONNECTED);
322
323        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
324    }
325
326    /**
327     *  Screen turned on while WiFi in disconnected state.
328     *
329     * Expected behavior: WifiConnectivityManager calls
330     * WifiStateMachine.startConnectToNetwork() with the
331     * expected candidate network ID and BSSID.
332     */
333    @Test
334    public void turnScreenOnWhenWifiInDisconnectedState() {
335        // Set WiFi to disconnected state
336        mWifiConnectivityManager.handleConnectionStateChanged(
337                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
338
339        // Set screen to on
340        mWifiConnectivityManager.handleScreenStateChanged(true);
341
342        verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
343                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
344    }
345
346    /**
347     *  Screen turned on while WiFi in connected state.
348     *
349     * Expected behavior: WifiConnectivityManager calls
350     * WifiStateMachine.startConnectToNetwork() with the
351     * expected candidate network ID and BSSID.
352     */
353    @Test
354    public void turnScreenOnWhenWifiInConnectedState() {
355        // Set WiFi to connected state
356        mWifiConnectivityManager.handleConnectionStateChanged(
357                WifiConnectivityManager.WIFI_STATE_CONNECTED);
358
359        // Set screen to on
360        mWifiConnectivityManager.handleScreenStateChanged(true);
361
362        verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
363                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
364    }
365
366    /**
367     *  Screen turned on while WiFi in connected state but
368     *  auto roaming is disabled.
369     *
370     * Expected behavior: WifiConnectivityManager doesn't invoke
371     * WifiStateMachine.startConnectToNetwork() because roaming
372     * is turned off.
373     */
374    @Test
375    public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() {
376        // Turn off auto roaming
377        when(mResource.getBoolean(
378                R.bool.config_wifi_framework_enable_associated_network_selection))
379                .thenReturn(false);
380        mWifiConnectivityManager = createConnectivityManager();
381
382        // Set WiFi to connected state
383        mWifiConnectivityManager.handleConnectionStateChanged(
384                WifiConnectivityManager.WIFI_STATE_CONNECTED);
385
386        // Set screen to on
387        mWifiConnectivityManager.handleScreenStateChanged(true);
388
389        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
390                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
391    }
392
393    /**
394     * Multiple back to back connection attempts within the rate interval should be rate limited.
395     *
396     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
397     * with the expected candidate network ID and BSSID for only the expected number of times within
398     * the given interval.
399     */
400    @Test
401    public void connectionAttemptRateLimitedWhenScreenOff() {
402        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
403        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
404        int numAttempts = 0;
405        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
406
407        mWifiConnectivityManager.handleScreenStateChanged(false);
408
409        // First attempt the max rate number of connections within the rate interval.
410        long currentTimeStamp = 0;
411        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
412            currentTimeStamp += connectionAttemptIntervals;
413            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
414            // Set WiFi to disconnected state to trigger PNO scan
415            mWifiConnectivityManager.handleConnectionStateChanged(
416                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
417            numAttempts++;
418        }
419        // Now trigger another connection attempt before the rate interval, this should be
420        // skipped because we've crossed rate limit.
421        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
422        // Set WiFi to disconnected state to trigger PNO scan
423        mWifiConnectivityManager.handleConnectionStateChanged(
424                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
425
426        // Verify that we attempt to connect upto the rate.
427        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
428                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
429    }
430
431    /**
432     * Multiple back to back connection attempts outside the rate interval should not be rate
433     * limited.
434     *
435     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
436     * with the expected candidate network ID and BSSID for only the expected number of times within
437     * the given interval.
438     */
439    @Test
440    public void connectionAttemptNotRateLimitedWhenScreenOff() {
441        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
442        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
443        int numAttempts = 0;
444        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
445
446        mWifiConnectivityManager.handleScreenStateChanged(false);
447
448        // First attempt the max rate number of connections within the rate interval.
449        long currentTimeStamp = 0;
450        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
451            currentTimeStamp += connectionAttemptIntervals;
452            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
453            // Set WiFi to disconnected state to trigger PNO scan
454            mWifiConnectivityManager.handleConnectionStateChanged(
455                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
456            numAttempts++;
457        }
458        // Now trigger another connection attempt after the rate interval, this should not be
459        // skipped because we should've evicted the older attempt.
460        when(mClock.getElapsedSinceBootMillis()).thenReturn(
461                currentTimeStamp + connectionAttemptIntervals * 2);
462        // Set WiFi to disconnected state to trigger PNO scan
463        mWifiConnectivityManager.handleConnectionStateChanged(
464                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
465        numAttempts++;
466
467        // Verify that all the connection attempts went through
468        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
469                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
470    }
471
472    /**
473     * Multiple back to back connection attempts after a user selection should not be rate limited.
474     *
475     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
476     * with the expected candidate network ID and BSSID for only the expected number of times within
477     * the given interval.
478     */
479    @Test
480    public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() {
481        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
482        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
483        int numAttempts = 0;
484        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
485
486        mWifiConnectivityManager.handleScreenStateChanged(false);
487
488        // First attempt the max rate number of connections within the rate interval.
489        long currentTimeStamp = 0;
490        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
491            currentTimeStamp += connectionAttemptIntervals;
492            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
493            // Set WiFi to disconnected state to trigger PNO scan
494            mWifiConnectivityManager.handleConnectionStateChanged(
495                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
496            numAttempts++;
497        }
498
499        mWifiConnectivityManager.setUserConnectChoice(CANDIDATE_NETWORK_ID);
500        mWifiConnectivityManager.prepareForForcedConnection(CANDIDATE_NETWORK_ID);
501
502        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
503            currentTimeStamp += connectionAttemptIntervals;
504            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
505            // Set WiFi to disconnected state to trigger PNO scan
506            mWifiConnectivityManager.handleConnectionStateChanged(
507                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
508            numAttempts++;
509        }
510
511        // Verify that all the connection attempts went through
512        verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
513                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
514    }
515
516    /**
517     *  PNO retry for low RSSI networks.
518     *
519     * Expected behavior: WifiConnectivityManager doubles the low RSSI
520     * network retry delay value after QNS skips the PNO scan results
521     * because of their low RSSI values.
522     */
523    @Test
524    @Ignore("b/32977707")
525    public void pnoRetryForLowRssiNetwork() {
526        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
527                anyBoolean(), anyBoolean())).thenReturn(null);
528
529        // Set screen to off
530        mWifiConnectivityManager.handleScreenStateChanged(false);
531
532        // Get the current retry delay value
533        int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager
534                .getLowRssiNetworkRetryDelay();
535
536        // Set WiFi to disconnected state to trigger PNO scan
537        mWifiConnectivityManager.handleConnectionStateChanged(
538                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
539
540        // Get the retry delay value after QNS didn't select a
541        // network candicate from the PNO scan results.
542        int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager
543                .getLowRssiNetworkRetryDelay();
544
545        assertEquals(lowRssiNetworkRetryDelayStartValue * 2,
546                lowRssiNetworkRetryDelayAfterPnoValue);
547    }
548
549    /**
550     * Ensure that the watchdog bite increments the "Pno bad" metric.
551     *
552     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
553     * a candidate while watchdog single scan did.
554     */
555    @Test
556    @Ignore("b/32977707")
557    public void watchdogBitePnoBadIncrementsMetrics() {
558        // Set screen to off
559        mWifiConnectivityManager.handleScreenStateChanged(false);
560
561        // Set WiFi to disconnected state to trigger PNO scan
562        mWifiConnectivityManager.handleConnectionStateChanged(
563                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
564
565        // Now fire the watchdog alarm and verify the metrics were incremented.
566        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
567        mLooper.dispatchAll();
568
569        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad();
570        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood();
571    }
572
573    /**
574     * Ensure that the watchdog bite increments the "Pno good" metric.
575     *
576     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
577     * a candidate which was the same with watchdog single scan.
578     */
579    @Test
580    @Ignore("b/32977707")
581    public void watchdogBitePnoGoodIncrementsMetrics() {
582        // Qns returns no candidate after watchdog single scan.
583        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
584                anyBoolean(), anyBoolean())).thenReturn(null);
585
586        // Set screen to off
587        mWifiConnectivityManager.handleScreenStateChanged(false);
588
589        // Set WiFi to disconnected state to trigger PNO scan
590        mWifiConnectivityManager.handleConnectionStateChanged(
591                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
592
593        // Now fire the watchdog alarm and verify the metrics were incremented.
594        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
595        mLooper.dispatchAll();
596
597        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood();
598        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad();
599    }
600
601    /**
602     *  Verify that scan interval for screen on and wifi disconnected scenario
603     *  is in the exponential backoff fashion.
604     *
605     * Expected behavior: WifiConnectivityManager doubles periodic
606     * scan interval.
607     */
608    @Test
609    public void checkPeriodicScanIntervalWhenDisconnected() {
610        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
611        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
612
613        // Set screen to ON
614        mWifiConnectivityManager.handleScreenStateChanged(true);
615
616        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
617        // by screen state change can settle
618        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
619        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
620
621        // Set WiFi to disconnected state to trigger periodic scan
622        mWifiConnectivityManager.handleConnectionStateChanged(
623                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
624
625        // Get the first periodic scan interval
626        long firstIntervalMs = mAlarmManager
627                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
628                - currentTimeStamp;
629        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
630
631        currentTimeStamp += firstIntervalMs;
632        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
633
634        // Now fire the first periodic scan alarm timer
635        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
636        mLooper.dispatchAll();
637
638        // Get the second periodic scan interval
639        long secondIntervalMs = mAlarmManager
640                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
641                - currentTimeStamp;
642
643        // Verify the intervals are exponential back off
644        assertEquals(firstIntervalMs * 2, secondIntervalMs);
645
646        currentTimeStamp += secondIntervalMs;
647        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
648
649        // Make sure we eventually stay at the maximum scan interval.
650        long intervalMs = 0;
651        for (int i = 0; i < 5; i++) {
652            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
653            mLooper.dispatchAll();
654            intervalMs = mAlarmManager
655                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
656                    - currentTimeStamp;
657            currentTimeStamp += intervalMs;
658            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
659        }
660
661        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
662    }
663
664    /**
665     *  Verify that scan interval for screen on and wifi connected scenario
666     *  is in the exponential backoff fashion.
667     *
668     * Expected behavior: WifiConnectivityManager doubles periodic
669     * scan interval.
670     */
671    @Test
672    public void checkPeriodicScanIntervalWhenConnected() {
673        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
674        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
675
676        // Set screen to ON
677        mWifiConnectivityManager.handleScreenStateChanged(true);
678
679        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
680        // by screen state change can settle
681        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
682        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
683
684        // Set WiFi to connected state to trigger periodic scan
685        mWifiConnectivityManager.handleConnectionStateChanged(
686                WifiConnectivityManager.WIFI_STATE_CONNECTED);
687
688        // Get the first periodic scan interval
689        long firstIntervalMs = mAlarmManager
690                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
691                - currentTimeStamp;
692        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
693
694        currentTimeStamp += firstIntervalMs;
695        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
696
697        // Now fire the first periodic scan alarm timer
698        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
699        mLooper.dispatchAll();
700
701        // Get the second periodic scan interval
702        long secondIntervalMs = mAlarmManager
703                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
704                - currentTimeStamp;
705
706        // Verify the intervals are exponential back off
707        assertEquals(firstIntervalMs * 2, secondIntervalMs);
708
709        currentTimeStamp += secondIntervalMs;
710        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
711
712        // Make sure we eventually stay at the maximum scan interval.
713        long intervalMs = 0;
714        for (int i = 0; i < 5; i++) {
715            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
716            mLooper.dispatchAll();
717            intervalMs = mAlarmManager
718                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
719                    - currentTimeStamp;
720            currentTimeStamp += intervalMs;
721            when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
722        }
723
724        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
725    }
726
727    /**
728     *  When screen on trigger a disconnected state change event then a connected state
729     *  change event back to back to verify that the minium scan interval is enforced.
730     *
731     * Expected behavior: WifiConnectivityManager start the second periodic single
732     * scan PERIODIC_SCAN_INTERVAL_MS after the first one.
733     */
734    @Test
735    public void checkMinimumPeriodicScanIntervalWhenScreenOnAndConnected() {
736        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
737        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
738
739        // Set screen to ON
740        mWifiConnectivityManager.handleScreenStateChanged(true);
741
742        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
743        // by screen state change can settle
744        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
745        long scanForDisconnectedTimeStamp = currentTimeStamp;
746        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
747
748        // Set WiFi to disconnected state which triggers a scan immediately
749        mWifiConnectivityManager.handleConnectionStateChanged(
750                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
751        verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
752
753        // Set up time stamp for when entering CONNECTED state
754        currentTimeStamp += 2000;
755        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
756
757        // Set WiFi to connected state to trigger its periodic scan
758        mWifiConnectivityManager.handleConnectionStateChanged(
759                WifiConnectivityManager.WIFI_STATE_CONNECTED);
760
761        // The very first scan triggered for connected state is actually via the alarm timer
762        // and it obeys the minimum scan interval
763        long firstScanForConnectedTimeStamp = mAlarmManager
764                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
765
766        // Verify that the first scan for connected state is scheduled PERIODIC_SCAN_INTERVAL_MS
767        // after the scan for disconnected state
768        assertEquals(firstScanForConnectedTimeStamp, scanForDisconnectedTimeStamp
769                + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
770    }
771
772    /**
773     *  When screen on trigger a connected state change event then a disconnected state
774     *  change event back to back to verify that a scan is fired immediately for the
775     *  disconnected state change event.
776     *
777     * Expected behavior: WifiConnectivityManager directly starts the periodic immediately
778     * for the disconnected state change event. The second scan for disconnected state is
779     * via alarm timer.
780     */
781    @Test
782    public void scanImmediatelyWhenScreenOnAndDisconnected() {
783        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
784        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
785
786        // Set screen to ON
787        mWifiConnectivityManager.handleScreenStateChanged(true);
788
789        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
790        // by screen state change can settle
791        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
792        long scanForConnectedTimeStamp = currentTimeStamp;
793        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
794
795        // Set WiFi to connected state to trigger the periodic scan
796        mWifiConnectivityManager.handleConnectionStateChanged(
797                WifiConnectivityManager.WIFI_STATE_CONNECTED);
798        verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
799
800        // Set up the time stamp for when entering DISCONNECTED state
801        currentTimeStamp += 2000;
802        long enteringDisconnectedStateTimeStamp = currentTimeStamp;
803        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
804
805        // Set WiFi to disconnected state to trigger its periodic scan
806        mWifiConnectivityManager.handleConnectionStateChanged(
807                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
808
809        // Verify the very first scan for DISCONNECTED state is fired immediately
810        verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject(), anyObject());
811        long secondScanForDisconnectedTimeStamp = mAlarmManager
812                .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
813
814        // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after
815        // entering DISCONNECTED state.
816        assertEquals(secondScanForDisconnectedTimeStamp, enteringDisconnectedStateTimeStamp
817                + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
818    }
819
820    /**
821     *  When screen on trigger a connection state change event and a forced connectivity
822     *  scan event back to back to verify that the minimum scan interval is not applied
823     *  in this scenario.
824     *
825     * Expected behavior: WifiConnectivityManager starts the second periodic single
826     * scan immediately.
827     */
828    @Test
829    public void checkMinimumPeriodicScanIntervalNotEnforced() {
830        long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
831        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
832
833        // Set screen to ON
834        mWifiConnectivityManager.handleScreenStateChanged(true);
835
836        // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
837        // by screen state change can settle
838        currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
839        long firstScanTimeStamp = currentTimeStamp;
840        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
841
842        // Set WiFi to connected state to trigger the periodic scan
843        mWifiConnectivityManager.handleConnectionStateChanged(
844                WifiConnectivityManager.WIFI_STATE_CONNECTED);
845
846        // Set the second scan attempt time stamp
847        currentTimeStamp += 2000;
848        when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
849
850        // Allow untrusted networks so WifiConnectivityManager starts a periodic scan
851        // immediately.
852        mWifiConnectivityManager.setUntrustedConnectionAllowed(true);
853
854        // Get the second periodic scan actual time stamp. Note, this scan is not
855        // started from the AlarmManager.
856        long secondScanTimeStamp = mWifiConnectivityManager.getLastPeriodicSingleScanTimeStamp();
857
858        // Verify that the second scan is fired immediately
859        assertEquals(secondScanTimeStamp, currentTimeStamp);
860    }
861
862    /**
863     * Verify that we perform full band scan when the currently connected network's tx/rx success
864     * rate is low.
865     *
866     * Expected behavior: WifiConnectivityManager does full band scan.
867     */
868    @Test
869    public void checkSingleScanSettingsWhenConnectedWithLowDataRate() {
870        mWifiInfo.txSuccessRate = 0;
871        mWifiInfo.rxSuccessRate = 0;
872
873        final HashSet<Integer> channelList = new HashSet<>();
874        channelList.add(1);
875        channelList.add(2);
876        channelList.add(3);
877
878        when(mWifiStateMachine.getCurrentWifiConfiguration())
879                .thenReturn(new WifiConfiguration());
880        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
881                anyInt())).thenReturn(channelList);
882
883        doAnswer(new AnswerWithArguments() {
884            public void answer(ScanSettings settings, ScanListener listener,
885                    WorkSource workSource) throws Exception {
886                assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
887                assertNull(settings.channels);
888            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
889
890        // Set screen to ON
891        mWifiConnectivityManager.handleScreenStateChanged(true);
892
893        // Set WiFi to connected state to trigger periodic scan
894        mWifiConnectivityManager.handleConnectionStateChanged(
895                WifiConnectivityManager.WIFI_STATE_CONNECTED);
896
897        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
898    }
899
900    /**
901     * Verify that we perform partial scan when the currently connected network's tx/rx success
902     * rate is high and when the currently connected network is present in scan
903     * cache in WifiConfigManager.
904     *
905     * Expected behavior: WifiConnectivityManager does full band scan.
906     */
907    @Test
908    public void checkSingleScanSettingsWhenConnectedWithHighDataRate() {
909        mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
910        mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
911
912        final HashSet<Integer> channelList = new HashSet<>();
913        channelList.add(1);
914        channelList.add(2);
915        channelList.add(3);
916
917        when(mWifiStateMachine.getCurrentWifiConfiguration())
918                .thenReturn(new WifiConfiguration());
919        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
920                anyInt())).thenReturn(channelList);
921
922        doAnswer(new AnswerWithArguments() {
923            public void answer(ScanSettings settings, ScanListener listener,
924                    WorkSource workSource) throws Exception {
925                assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED);
926                assertEquals(settings.channels.length, channelList.size());
927                for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) {
928                    assertTrue(channelList.contains(settings.channels[chanIdx].frequency));
929                }
930            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
931
932        // Set screen to ON
933        mWifiConnectivityManager.handleScreenStateChanged(true);
934
935        // Set WiFi to connected state to trigger periodic scan
936        mWifiConnectivityManager.handleConnectionStateChanged(
937                WifiConnectivityManager.WIFI_STATE_CONNECTED);
938
939        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
940    }
941
942    /**
943     * Verify that we fall back to full band scan when the currently connected network's tx/rx
944     * success rate is high and the currently connected network is not present in scan cache in
945     * WifiConfigManager. This is simulated by returning an empty hashset in |makeChannelList|.
946     *
947     * Expected behavior: WifiConnectivityManager does full band scan.
948     */
949    @Test
950    public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() {
951        mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
952        mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
953
954        final HashSet<Integer> channelList = new HashSet<>();
955
956        when(mWifiStateMachine.getCurrentWifiConfiguration())
957                .thenReturn(new WifiConfiguration());
958        when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
959                anyInt())).thenReturn(channelList);
960
961        doAnswer(new AnswerWithArguments() {
962            public void answer(ScanSettings settings, ScanListener listener,
963                    WorkSource workSource) throws Exception {
964                assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
965                assertNull(settings.channels);
966            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
967
968        // Set screen to ON
969        mWifiConnectivityManager.handleScreenStateChanged(true);
970
971        // Set WiFi to connected state to trigger periodic scan
972        mWifiConnectivityManager.handleConnectionStateChanged(
973                WifiConnectivityManager.WIFI_STATE_CONNECTED);
974
975        verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
976    }
977
978    /**
979     *  Verify that we retry connectivity scan up to MAX_SCAN_RESTART_ALLOWED times
980     *  when Wifi somehow gets into a bad state and fails to scan.
981     *
982     * Expected behavior: WifiConnectivityManager schedules connectivity scan
983     * MAX_SCAN_RESTART_ALLOWED times.
984     */
985    @Test
986    public void checkMaximumScanRetry() {
987        // Set screen to ON
988        mWifiConnectivityManager.handleScreenStateChanged(true);
989
990        doAnswer(new AnswerWithArguments() {
991            public void answer(ScanSettings settings, ScanListener listener,
992                    WorkSource workSource) throws Exception {
993                listener.onFailure(-1, "ScanFailure");
994            }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
995
996        // Set WiFi to disconnected state to trigger the single scan based periodic scan
997        mWifiConnectivityManager.handleConnectionStateChanged(
998                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
999
1000        // Fire the alarm timer 2x timers
1001        for (int i = 0; i < (WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED * 2); i++) {
1002            mAlarmManager.dispatch(WifiConnectivityManager.RESTART_SINGLE_SCAN_TIMER_TAG);
1003            mLooper.dispatchAll();
1004        }
1005
1006        // Verify that the connectivity scan has been retried for MAX_SCAN_RESTART_ALLOWED
1007        // times. Note, WifiScanner.startScan() is invoked MAX_SCAN_RESTART_ALLOWED + 1 times.
1008        // The very first scan is the initial one, and the other MAX_SCAN_RESTART_ALLOWED
1009        // are the retrial ones.
1010        verify(mWifiScanner, times(WifiConnectivityManager.MAX_SCAN_RESTART_ALLOWED + 1)).startScan(
1011                anyObject(), anyObject(), anyObject());
1012    }
1013
1014    /**
1015     * Listen to scan results not requested by WifiConnectivityManager and
1016     * act on them.
1017     *
1018     * Expected behavior: WifiConnectivityManager calls
1019     * WifiStateMachine.startConnectToNetwork() with the
1020     * expected candidate network ID and BSSID.
1021     */
1022    @Test
1023    public void listenToAllSingleScanResults() {
1024        ScanSettings settings = new ScanSettings();
1025        ScanListener scanListener = mock(ScanListener.class);
1026
1027        // Request a single scan outside of WifiConnectivityManager.
1028        mWifiScanner.startScan(settings, scanListener, WIFI_WORK_SOURCE);
1029
1030        // Verify that WCM receives the scan results and initiates a connection
1031        // to the network.
1032        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1033    }
1034
1035    /**
1036     *  Verify that a forced connectivity scan waits for full band scan
1037     *  results.
1038     *
1039     * Expected behavior: WifiConnectivityManager doesn't invoke
1040     * WifiStateMachine.startConnectToNetwork() when full band scan
1041     * results are not available.
1042     */
1043    @Test
1044    public void waitForFullBandScanResults() {
1045        // Set WiFi to connected state.
1046        mWifiConnectivityManager.handleConnectionStateChanged(
1047                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1048
1049        // Set up as partial scan results.
1050        when(mScanData.isAllChannelsScanned()).thenReturn(false);
1051
1052        // Force a connectivity scan which enables WifiConnectivityManager
1053        // to wait for full band scan results.
1054        mWifiConnectivityManager.forceConnectivityScan();
1055
1056        // No roaming because no full band scan results.
1057        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
1058                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1059
1060        // Set up as full band scan results.
1061        when(mScanData.isAllChannelsScanned()).thenReturn(true);
1062
1063        // Force a connectivity scan which enables WifiConnectivityManager
1064        // to wait for full band scan results.
1065        mWifiConnectivityManager.forceConnectivityScan();
1066
1067        // Roaming attempt because full band scan results are available.
1068        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1069    }
1070
1071    /**
1072     *  Verify the BSSID blacklist implementation.
1073     *
1074     * Expected behavior: A BSSID gets blacklisted after being disabled
1075     * for 3 times, and becomes available after being re-enabled. Firmware
1076     * controlled roaming is supported, its roaming configuration needs to be
1077     * updated as well.
1078     */
1079    @Test
1080    public void blacklistAndReenableBssid() {
1081        String bssid = "6c:f3:7f:ae:8c:f3";
1082
1083        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1084        // Verify that a BSSID gets blacklisted only after being disabled
1085        // for BSSID_BLACKLIST_THRESHOLD times for reasons other than
1086        // REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA.
1087        for (int i = 0; i < WifiConnectivityManager.BSSID_BLACKLIST_THRESHOLD; i++) {
1088            assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1089            mWifiConnectivityManager.trackBssid(bssid, false, 1);
1090        }
1091
1092        // Verify the BSSID is now blacklisted.
1093        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1094        // Verify the BSSID gets sent to firmware.
1095        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1096                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1097        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1098        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1099
1100        // Re-enable the bssid.
1101        mWifiConnectivityManager.trackBssid(bssid, true, 1);
1102
1103        // Verify the bssid is no longer blacklisted.
1104        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1105        // Verify the BSSID gets cleared from firmware.
1106        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1107                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1108        assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
1109        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1110    }
1111
1112    /**
1113     *  Verify that a network gets blacklisted immediately if it is unable
1114     *  to handle new stations.
1115     */
1116    @Test
1117    public void blacklistNetworkImmediatelyIfApHasNoCapacityForNewStation() {
1118        String bssid = "6c:f3:7f:ae:8c:f3";
1119
1120        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1121        // Blacklist the BSSID
1122        mWifiConnectivityManager.trackBssid(bssid, false,
1123                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1124
1125        // Verify the BSSID is now blacklisted.
1126        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1127        // Verify the BSSID gets sent to firmware.
1128        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1129                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1130        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1131        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1132    }
1133
1134    /**
1135     *  Verify that a blacklisted BSSID becomes available only after
1136     *  BSSID_BLACKLIST_EXPIRE_TIME_MS.
1137     */
1138    @Test
1139    public void verifyBlacklistRefreshedAfterScanResults() {
1140        String bssid = "6c:f3:7f:ae:8c:f3";
1141
1142        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1143        // Blacklist the BSSID.
1144        mWifiConnectivityManager.trackBssid(bssid, false,
1145                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1146
1147        // Verify the BSSID is now blacklisted.
1148        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1149        // Verify the BSSID gets sent to firmware.
1150        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1151                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1152        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1153        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1154
1155        // Force a connectivity scan in less than BSSID_BLACKLIST_EXPIRE_TIME_MS.
1156        // Arrival of scan results will trigger WifiConnectivityManager to refresh its
1157        // BSSID blacklist. Verify that the blacklisted BSSId is not freed because
1158        // its blacklist expiration time hasn't reached yet.
1159        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
1160                + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS / 2);
1161        mWifiConnectivityManager.forceConnectivityScan();
1162        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1163
1164        // Force another connectivity scan at BSSID_BLACKLIST_EXPIRE_TIME_MS from when the
1165        // BSSID was blacklisted. Verify that the blacklisted BSSId is freed.
1166        when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
1167                + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS);
1168        mWifiConnectivityManager.forceConnectivityScan();
1169
1170        // Verify the BSSID is no longer blacklisted.
1171        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1172        // Verify the BSSID gets cleared from firmware.
1173        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1174                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1175        assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
1176        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1177    }
1178
1179    /**
1180     *  Verify that BSSID blacklist gets cleared when exiting Wifi client mode.
1181     */
1182    @Test
1183    public void clearBssidBlacklistWhenExitingWifiClientMode() {
1184        String bssid = "6c:f3:7f:ae:8c:f3";
1185
1186        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1187
1188        // Blacklist the BSSID.
1189        mWifiConnectivityManager.trackBssid(bssid, false,
1190                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1191
1192        // Verify the BSSID is now blacklisted.
1193        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1194        // Verify the BSSID gets sent to firmware.
1195        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1196                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1197        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1198        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1199
1200        // Exit Wifi client mode.
1201        mWifiConnectivityManager.setWifiEnabled(false);
1202
1203        // Verify the BSSID blacklist is empty.
1204        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1205        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1206                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1207        assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
1208        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1209    }
1210
1211    /**
1212     *  Verify that BSSID blacklist gets cleared when preparing for a forced connection
1213     *  initiated by user/app.
1214     */
1215    @Test
1216    public void clearBssidBlacklistWhenPreparingForForcedConnection() {
1217        String bssid = "6c:f3:7f:ae:8c:f3";
1218
1219        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1220
1221        // Blacklist the BSSID.
1222        mWifiConnectivityManager.trackBssid(bssid, false,
1223                WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1224
1225        // Verify the BSSID is now blacklisted.
1226        assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
1227        // Verify the BSSID gets sent to firmware.
1228        verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
1229                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1230        assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
1231        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1232
1233        // Prepare for a forced connection attempt.
1234        mWifiConnectivityManager.prepareForForcedConnection(1);
1235
1236        // Verify the BSSID blacklist is empty.
1237        assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
1238        verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
1239                mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1240        assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
1241        assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1242    }
1243
1244    /**
1245    /**
1246     *  Verify that BSSID blacklist gets trimmed down to fit firmware capability.
1247     */
1248    @Test
1249    public void trimDownBssidBlacklistForFirmware() {
1250        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1251
1252        // Blacklist more than MAX_BSSID_BLACKLIST_SIZE BSSIDs.
1253        for (int i = 0; i < MAX_BSSID_BLACKLIST_SIZE + 6; i++) {
1254            StringBuilder bssid = new StringBuilder("55:44:33:22:11:00");
1255            bssid.setCharAt(16, (char) ('0' + i));
1256            mWifiConnectivityManager.trackBssid(bssid.toString(), false,
1257                    WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
1258            // Verify that up to MAX_BSSID_BLACKLIST_SIZE BSSIDs gets sent to firmware.
1259            verify(mWifiConnectivityHelper, times(i + 1)).setFirmwareRoamingConfiguration(
1260                    mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
1261            assertEquals((i + 1) <  MAX_BSSID_BLACKLIST_SIZE ? (i + 1) : MAX_BSSID_BLACKLIST_SIZE,
1262                    mBssidBlacklistCaptor.getValue().size());
1263            assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
1264        }
1265    }
1266
1267    /**
1268     * When WifiConnectivityManager is on and Wifi client mode is enabled, framework
1269     * queries firmware via WifiConnectivityHelper to check if firmware roaming is
1270     * supported and its capability.
1271     *
1272     * Expected behavior: WifiConnectivityManager#setWifiEnabled calls into
1273     * WifiConnectivityHelper#getFirmwareRoamingInfo
1274     */
1275    @Test
1276    public void verifyGetFirmwareRoamingInfoIsCalledWhenEnableWiFiAndWcmOn() {
1277        reset(mWifiConnectivityHelper);
1278        // WifiConnectivityManager is on by default
1279        mWifiConnectivityManager.setWifiEnabled(true);
1280        verify(mWifiConnectivityHelper).getFirmwareRoamingInfo();
1281    }
1282
1283    /**
1284     * When WifiConnectivityManager is off,  verify that framework does not
1285     * query firmware via WifiConnectivityHelper to check if firmware roaming is
1286     * supported and its capability when enabling Wifi client mode.
1287     *
1288     * Expected behavior: WifiConnectivityManager#setWifiEnabled does not call into
1289     * WifiConnectivityHelper#getFirmwareRoamingInfo
1290     */
1291    @Test
1292    public void verifyGetFirmwareRoamingInfoIsNotCalledWhenEnableWiFiAndWcmOff() {
1293        reset(mWifiConnectivityHelper);
1294        mWifiConnectivityManager.enable(false);
1295        mWifiConnectivityManager.setWifiEnabled(true);
1296        verify(mWifiConnectivityHelper, times(0)).getFirmwareRoamingInfo();
1297    }
1298
1299    /*
1300     * Firmware supports controlled roaming.
1301     * Connect to a network which doesn't have a config specified BSSID.
1302     *
1303     * Expected behavior: WifiConnectivityManager calls
1304     * WifiStateMachine.startConnectToNetwork() with the
1305     * expected candidate network ID, and the BSSID value should be
1306     * 'any' since firmware controls the roaming.
1307     */
1308    @Test
1309    public void useAnyBssidToConnectWhenFirmwareRoamingOnAndConfigHasNoBssidSpecified() {
1310        // Firmware controls roaming
1311        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1312
1313        // Set screen to on
1314        mWifiConnectivityManager.handleScreenStateChanged(true);
1315
1316        // Set WiFi to disconnected state
1317        mWifiConnectivityManager.handleConnectionStateChanged(
1318                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1319
1320        verify(mWifiStateMachine).startConnectToNetwork(
1321                CANDIDATE_NETWORK_ID, WifiStateMachine.SUPPLICANT_BSSID_ANY);
1322    }
1323
1324    /*
1325     * Firmware supports controlled roaming.
1326     * Connect to a network which has a config specified BSSID.
1327     *
1328     * Expected behavior: WifiConnectivityManager calls
1329     * WifiStateMachine.startConnectToNetwork() with the
1330     * expected candidate network ID, and the BSSID value should be
1331     * the config specified one.
1332     */
1333    @Test
1334    public void useConfigSpecifiedBssidToConnectWhenFirmwareRoamingOn() {
1335        // Firmware controls roaming
1336        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1337
1338        // Set up the candidate configuration such that it has a BSSID specified.
1339        WifiConfiguration candidate = generateWifiConfig(
1340                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1341        candidate.BSSID = CANDIDATE_BSSID; // config specified
1342        ScanResult candidateScanResult = new ScanResult();
1343        candidateScanResult.SSID = CANDIDATE_SSID;
1344        candidateScanResult.BSSID = CANDIDATE_BSSID;
1345        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1346
1347        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1348                anyBoolean(), anyBoolean())).thenReturn(candidate);
1349
1350        // Set screen to on
1351        mWifiConnectivityManager.handleScreenStateChanged(true);
1352
1353        // Set WiFi to disconnected state
1354        mWifiConnectivityManager.handleConnectionStateChanged(
1355                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1356
1357        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1358    }
1359
1360    /*
1361     * Firmware does not support controlled roaming.
1362     * Connect to a network which doesn't have a config specified BSSID.
1363     *
1364     * Expected behavior: WifiConnectivityManager calls
1365     * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
1366     * and the BSSID value should be the candidate scan result specified.
1367     */
1368    @Test
1369    public void useScanResultBssidToConnectWhenFirmwareRoamingOffAndConfigHasNoBssidSpecified() {
1370        // Set screen to on
1371        mWifiConnectivityManager.handleScreenStateChanged(true);
1372
1373        // Set WiFi to disconnected state
1374        mWifiConnectivityManager.handleConnectionStateChanged(
1375                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1376
1377        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1378    }
1379
1380    /*
1381     * Firmware does not support controlled roaming.
1382     * Connect to a network which has a config specified BSSID.
1383     *
1384     * Expected behavior: WifiConnectivityManager calls
1385     * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
1386     * and the BSSID value should be the config specified one.
1387     */
1388    @Test
1389    public void useConfigSpecifiedBssidToConnectionWhenFirmwareRoamingOff() {
1390        // Set up the candidate configuration such that it has a BSSID specified.
1391        WifiConfiguration candidate = generateWifiConfig(
1392                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1393        candidate.BSSID = CANDIDATE_BSSID; // config specified
1394        ScanResult candidateScanResult = new ScanResult();
1395        candidateScanResult.SSID = CANDIDATE_SSID;
1396        candidateScanResult.BSSID = CANDIDATE_BSSID;
1397        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1398
1399        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1400                anyBoolean(), anyBoolean())).thenReturn(candidate);
1401
1402        // Set screen to on
1403        mWifiConnectivityManager.handleScreenStateChanged(true);
1404
1405        // Set WiFi to disconnected state
1406        mWifiConnectivityManager.handleConnectionStateChanged(
1407                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1408
1409        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1410    }
1411
1412    /**
1413     * Firmware does not support controlled roaming.
1414     * WiFi in connected state, framework triggers roaming.
1415     *
1416     * Expected behavior: WifiConnectivityManager invokes
1417     * WifiStateMachine.startRoamToNetwork().
1418     */
1419    @Test
1420    public void frameworkInitiatedRoaming() {
1421        // Mock the currently connected network which has the same networkID and
1422        // SSID as the one to be selected.
1423        WifiConfiguration currentNetwork = generateWifiConfig(
1424                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1425        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1426
1427        // Set WiFi to connected state
1428        mWifiConnectivityManager.handleConnectionStateChanged(
1429                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1430
1431        // Set screen to on
1432        mWifiConnectivityManager.handleScreenStateChanged(true);
1433
1434        verify(mWifiStateMachine).startRoamToNetwork(eq(CANDIDATE_NETWORK_ID),
1435                mCandidateScanResultCaptor.capture());
1436        assertEquals(mCandidateScanResultCaptor.getValue().BSSID, CANDIDATE_BSSID);
1437    }
1438
1439    /**
1440     * Firmware supports controlled roaming.
1441     * WiFi in connected state, framework does not trigger roaming
1442     * as it's handed off to the firmware.
1443     *
1444     * Expected behavior: WifiConnectivityManager doesn't invoke
1445     * WifiStateMachine.startRoamToNetwork().
1446     */
1447    @Test
1448    public void noFrameworkRoamingIfConnectedAndFirmwareRoamingSupported() {
1449        // Mock the currently connected network which has the same networkID and
1450        // SSID as the one to be selected.
1451        WifiConfiguration currentNetwork = generateWifiConfig(
1452                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1453        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1454
1455        // Firmware controls roaming
1456        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
1457
1458        // Set WiFi to connected state
1459        mWifiConnectivityManager.handleConnectionStateChanged(
1460                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1461
1462        // Set screen to on
1463        mWifiConnectivityManager.handleScreenStateChanged(true);
1464
1465        verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
1466    }
1467
1468    /*
1469     * Wifi in disconnected state. Drop the connection attempt if the recommended
1470     * network configuration has a BSSID specified but the scan result BSSID doesn't
1471     * match it.
1472     *
1473     * Expected behavior: WifiConnectivityManager doesn't invoke
1474     * WifiStateMachine.startConnectToNetwork().
1475     */
1476    @Test
1477    public void dropConnectAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
1478        // Set up the candidate configuration such that it has a BSSID specified.
1479        WifiConfiguration candidate = generateWifiConfig(
1480                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1481        candidate.BSSID = CANDIDATE_BSSID; // config specified
1482        ScanResult candidateScanResult = new ScanResult();
1483        candidateScanResult.SSID = CANDIDATE_SSID;
1484        // Set up the scan result BSSID to be different from the config specified one.
1485        candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
1486        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1487
1488        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1489                anyBoolean(), anyBoolean())).thenReturn(candidate);
1490
1491        // Set screen to on
1492        mWifiConnectivityManager.handleScreenStateChanged(true);
1493
1494        // Set WiFi to disconnected state
1495        mWifiConnectivityManager.handleConnectionStateChanged(
1496                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
1497
1498        verify(mWifiStateMachine, times(0)).startConnectToNetwork(
1499                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
1500    }
1501
1502    /*
1503     * Wifi in connected state. Drop the roaming attempt if the recommended
1504     * network configuration has a BSSID specified but the scan result BSSID doesn't
1505     * match it.
1506     *
1507     * Expected behavior: WifiConnectivityManager doesn't invoke
1508     * WifiStateMachine.startRoamToNetwork().
1509     */
1510    @Test
1511    public void dropRoamingAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
1512        // Mock the currently connected network which has the same networkID and
1513        // SSID as the one to be selected.
1514        WifiConfiguration currentNetwork = generateWifiConfig(
1515                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1516        when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
1517
1518        // Set up the candidate configuration such that it has a BSSID specified.
1519        WifiConfiguration candidate = generateWifiConfig(
1520                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
1521        candidate.BSSID = CANDIDATE_BSSID; // config specified
1522        ScanResult candidateScanResult = new ScanResult();
1523        candidateScanResult.SSID = CANDIDATE_SSID;
1524        // Set up the scan result BSSID to be different from the config specified one.
1525        candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
1526        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
1527
1528        when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
1529                anyBoolean(), anyBoolean())).thenReturn(candidate);
1530
1531        // Set WiFi to connected state
1532        mWifiConnectivityManager.handleConnectionStateChanged(
1533                WifiConnectivityManager.WIFI_STATE_CONNECTED);
1534
1535        // Set screen to on
1536        mWifiConnectivityManager.handleScreenStateChanged(true);
1537
1538        verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
1539    }
1540
1541    /**
1542     *  Dump local log buffer.
1543     *
1544     * Expected behavior: Logs dumped from WifiConnectivityManager.dump()
1545     * contain the message we put in mLocalLog.
1546     */
1547    @Test
1548    public void dumpLocalLog() {
1549        final String localLogMessage = "This is a message from the test";
1550        mLocalLog.log(localLogMessage);
1551
1552        StringWriter sw = new StringWriter();
1553        PrintWriter pw = new PrintWriter(sw);
1554        mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
1555        assertTrue(sw.toString().contains(localLogMessage));
1556    }
1557}
1558