Home | History | Annotate | Download | only in wifi
      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 
     17 package com.android.server.wifi;
     18 
     19 import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
     20 
     21 import static org.junit.Assert.*;
     22 import static org.mockito.Mockito.*;
     23 
     24 import android.content.Context;
     25 import android.content.res.Resources;
     26 import android.net.wifi.ScanResult;
     27 import android.net.wifi.ScanResult.InformationElement;
     28 import android.net.wifi.SupplicantState;
     29 import android.net.wifi.WifiConfiguration;
     30 import android.net.wifi.WifiInfo;
     31 import android.net.wifi.WifiManager;
     32 import android.net.wifi.WifiScanner;
     33 import android.net.wifi.WifiScanner.PnoScanListener;
     34 import android.net.wifi.WifiScanner.PnoSettings;
     35 import android.net.wifi.WifiScanner.ScanListener;
     36 import android.net.wifi.WifiScanner.ScanSettings;
     37 import android.net.wifi.WifiSsid;
     38 import android.os.SystemClock;
     39 import android.os.WorkSource;
     40 import android.test.suitebuilder.annotation.SmallTest;
     41 
     42 import com.android.internal.R;
     43 import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
     44 
     45 import org.junit.After;
     46 import org.junit.Before;
     47 import org.junit.Test;
     48 
     49 import java.nio.charset.StandardCharsets;
     50 import java.util.ArrayList;
     51 import java.util.HashSet;
     52 import java.util.concurrent.atomic.AtomicInteger;
     53 
     54 /**
     55  * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
     56  */
     57 @SmallTest
     58 public class WifiConnectivityManagerTest {
     59 
     60     /**
     61      * Called before each test
     62      */
     63     @Before
     64     public void setUp() throws Exception {
     65         mWifiInjector = mockWifiInjector();
     66         mResource = mockResource();
     67         mAlarmManager = new MockAlarmManager();
     68         mContext = mockContext();
     69         mWifiStateMachine = mockWifiStateMachine();
     70         mWifiConfigManager = mockWifiConfigManager();
     71         mWifiInfo = getWifiInfo();
     72         mWifiScanner = mockWifiScanner();
     73         mWifiQNS = mockWifiQualifiedNetworkSelector();
     74         mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine,
     75                 mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector,
     76                 mLooper.getLooper());
     77         mWifiConnectivityManager.setWifiEnabled(true);
     78         when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
     79     }
     80 
     81     /**
     82      * Called after each test
     83      */
     84     @After
     85     public void cleanup() {
     86         validateMockitoUsage();
     87     }
     88 
     89     private Resources mResource;
     90     private Context mContext;
     91     private MockAlarmManager mAlarmManager;
     92     private MockLooper mLooper = new MockLooper();
     93     private WifiConnectivityManager mWifiConnectivityManager;
     94     private WifiQualifiedNetworkSelector mWifiQNS;
     95     private WifiStateMachine mWifiStateMachine;
     96     private WifiScanner mWifiScanner;
     97     private WifiConfigManager mWifiConfigManager;
     98     private WifiInfo mWifiInfo;
     99     private Clock mClock = mock(Clock.class);
    100     private WifiLastResortWatchdog mWifiLastResortWatchdog;
    101     private WifiMetrics mWifiMetrics;
    102     private WifiInjector mWifiInjector;
    103 
    104     private static final int CANDIDATE_NETWORK_ID = 0;
    105     private static final String CANDIDATE_SSID = "\"AnSsid\"";
    106     private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3";
    107     private static final String TAG = "WifiConnectivityManager Unit Test";
    108     private static final long CURRENT_SYSTEM_TIME_MS = 1000;
    109 
    110     Resources mockResource() {
    111         Resources resource = mock(Resources.class);
    112 
    113         when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
    114         when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
    115 
    116         return resource;
    117     }
    118 
    119     Context mockContext() {
    120         Context context = mock(Context.class);
    121 
    122         when(context.getResources()).thenReturn(mResource);
    123         when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
    124                 mAlarmManager.getAlarmManager());
    125 
    126         return context;
    127     }
    128 
    129     WifiScanner mockWifiScanner() {
    130         WifiScanner scanner = mock(WifiScanner.class);
    131 
    132         // dummy scan results. QNS PeriodicScanListener bulids scanDetails from
    133         // the fullScanResult and doesn't really use results
    134         final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1];
    135 
    136         // do a synchronous answer for the ScanListener callbacks
    137         doAnswer(new AnswerWithArguments() {
    138                 public void answer(ScanSettings settings, ScanListener listener,
    139                         WorkSource workSource) throws Exception {
    140                     listener.onResults(scanDatas);
    141                 }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
    142 
    143         doAnswer(new AnswerWithArguments() {
    144                 public void answer(ScanSettings settings, ScanListener listener,
    145                         WorkSource workSource) throws Exception {
    146                     listener.onResults(scanDatas);
    147                 }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
    148 
    149         // This unfortunately needs to be a somewhat valid scan result, otherwise
    150         // |ScanDetailUtil.toScanDetail| raises exceptions.
    151         final ScanResult[] scanResults = new ScanResult[1];
    152         scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
    153                 CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps",
    154                 -78, 2450, 1025, 22, 33, 20, 0, 0, true);
    155         scanResults[0].informationElements = new InformationElement[1];
    156         scanResults[0].informationElements[0] = new InformationElement();
    157         scanResults[0].informationElements[0].id = InformationElement.EID_SSID;
    158         scanResults[0].informationElements[0].bytes =
    159                 CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
    160 
    161         doAnswer(new AnswerWithArguments() {
    162             public void answer(ScanSettings settings, PnoSettings pnoSettings,
    163                     PnoScanListener listener) throws Exception {
    164                 listener.onPnoNetworkFound(scanResults);
    165             }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject());
    166 
    167         doAnswer(new AnswerWithArguments() {
    168             public void answer(ScanSettings settings, PnoSettings pnoSettings,
    169                     PnoScanListener listener) throws Exception {
    170                 listener.onPnoNetworkFound(scanResults);
    171             }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject());
    172 
    173         return scanner;
    174     }
    175 
    176     WifiStateMachine mockWifiStateMachine() {
    177         WifiStateMachine stateMachine = mock(WifiStateMachine.class);
    178 
    179         when(stateMachine.getFrequencyBand()).thenReturn(1);
    180         when(stateMachine.isLinkDebouncing()).thenReturn(false);
    181         when(stateMachine.isConnected()).thenReturn(false);
    182         when(stateMachine.isDisconnected()).thenReturn(true);
    183         when(stateMachine.isSupplicantTransientState()).thenReturn(false);
    184 
    185         return stateMachine;
    186     }
    187 
    188     WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() {
    189         WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class);
    190 
    191         WifiConfiguration candidate = generateWifiConfig(
    192                 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
    193         candidate.BSSID = CANDIDATE_BSSID;
    194         ScanResult candidateScanResult = new ScanResult();
    195         candidateScanResult.SSID = CANDIDATE_SSID;
    196         candidateScanResult.BSSID = CANDIDATE_BSSID;
    197         candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
    198 
    199         when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
    200               anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate);
    201         return qns;
    202     }
    203 
    204     WifiInfo getWifiInfo() {
    205         WifiInfo wifiInfo = new WifiInfo();
    206 
    207         wifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
    208         wifiInfo.setBSSID(null);
    209         wifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
    210 
    211         return wifiInfo;
    212     }
    213 
    214     WifiConfigManager mockWifiConfigManager() {
    215         WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
    216 
    217         when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null);
    218         wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger(
    219                 WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND);
    220         wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger(
    221                 WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD);
    222 
    223         // Pass dummy pno network list, otherwise Pno scan requests will not be triggered.
    224         PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID);
    225         ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>();
    226         pnoNetworkList.add(pnoNetwork);
    227         when(wifiConfigManager.retrieveDisconnectedPnoNetworkList()).thenReturn(pnoNetworkList);
    228         when(wifiConfigManager.retrieveConnectedPnoNetworkList()).thenReturn(pnoNetworkList);
    229 
    230         return wifiConfigManager;
    231     }
    232 
    233     WifiInjector mockWifiInjector() {
    234         WifiInjector wifiInjector = mock(WifiInjector.class);
    235         mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class);
    236         mWifiMetrics = mock(WifiMetrics.class);
    237         when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
    238         when(wifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
    239         when(wifiInjector.getClock()).thenReturn(mClock);
    240         return wifiInjector;
    241     }
    242 
    243     /**
    244      *  Wifi enters disconnected state while screen is on.
    245      *
    246      * Expected behavior: WifiConnectivityManager calls
    247      * WifiStateMachine.autoConnectToNetwork() with the
    248      * expected candidate network ID and BSSID.
    249      */
    250     @Test
    251     public void enterWifiDisconnectedStateWhenScreenOn() {
    252         // Set screen to on
    253         mWifiConnectivityManager.handleScreenStateChanged(true);
    254 
    255         // Set WiFi to disconnected state
    256         mWifiConnectivityManager.handleConnectionStateChanged(
    257                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    258 
    259         verify(mWifiStateMachine).autoConnectToNetwork(
    260                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    261     }
    262 
    263     /**
    264      *  Wifi enters connected state while screen is on.
    265      *
    266      * Expected behavior: WifiConnectivityManager calls
    267      * WifiStateMachine.autoConnectToNetwork() with the
    268      * expected candidate network ID and BSSID.
    269      */
    270     @Test
    271     public void enterWifiConnectedStateWhenScreenOn() {
    272         // Set screen to on
    273         mWifiConnectivityManager.handleScreenStateChanged(true);
    274 
    275         // Set WiFi to connected state
    276         mWifiConnectivityManager.handleConnectionStateChanged(
    277                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    278 
    279         verify(mWifiStateMachine).autoConnectToNetwork(
    280                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    281     }
    282 
    283     /**
    284      *  Screen turned on while WiFi in disconnected state.
    285      *
    286      * Expected behavior: WifiConnectivityManager calls
    287      * WifiStateMachine.autoConnectToNetwork() with the
    288      * expected candidate network ID and BSSID.
    289      */
    290     @Test
    291     public void turnScreenOnWhenWifiInDisconnectedState() {
    292         // Set WiFi to disconnected state
    293         mWifiConnectivityManager.handleConnectionStateChanged(
    294                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    295 
    296         // Set screen to on
    297         mWifiConnectivityManager.handleScreenStateChanged(true);
    298 
    299         verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
    300                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    301     }
    302 
    303     /**
    304      *  Screen turned on while WiFi in connected state.
    305      *
    306      * Expected behavior: WifiConnectivityManager calls
    307      * WifiStateMachine.autoConnectToNetwork() with the
    308      * expected candidate network ID and BSSID.
    309      */
    310     @Test
    311     public void turnScreenOnWhenWifiInConnectedState() {
    312         // Set WiFi to connected state
    313         mWifiConnectivityManager.handleConnectionStateChanged(
    314                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    315 
    316         // Set screen to on
    317         mWifiConnectivityManager.handleScreenStateChanged(true);
    318 
    319         verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
    320                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    321     }
    322 
    323     /**
    324      * Multiple back to back connection attempts within the rate interval should be rate limited.
    325      *
    326      * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
    327      * with the expected candidate network ID and BSSID for only the expected number of times within
    328      * the given interval.
    329      */
    330     @Test
    331     public void connectionAttemptRateLimitedWhenScreenOff() {
    332         int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
    333         int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
    334         int numAttempts = 0;
    335         int connectionAttemptIntervals = timeInterval / maxAttemptRate;
    336 
    337         mWifiConnectivityManager.handleScreenStateChanged(false);
    338 
    339         // First attempt the max rate number of connections within the rate interval.
    340         long currentTimeStamp = 0;
    341         for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
    342             currentTimeStamp += connectionAttemptIntervals;
    343             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    344             // Set WiFi to disconnected state to trigger PNO scan
    345             mWifiConnectivityManager.handleConnectionStateChanged(
    346                     WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    347             numAttempts++;
    348         }
    349         // Now trigger another connection attempt before the rate interval, this should be
    350         // skipped because we've crossed rate limit.
    351         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    352         // Set WiFi to disconnected state to trigger PNO scan
    353         mWifiConnectivityManager.handleConnectionStateChanged(
    354                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    355 
    356         // Verify that we attempt to connect upto the rate.
    357         verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
    358                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    359     }
    360 
    361     /**
    362      * Multiple back to back connection attempts outside the rate interval should not be rate
    363      * limited.
    364      *
    365      * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
    366      * with the expected candidate network ID and BSSID for only the expected number of times within
    367      * the given interval.
    368      */
    369     @Test
    370     public void connectionAttemptNotRateLimitedWhenScreenOff() {
    371         int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
    372         int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
    373         int numAttempts = 0;
    374         int connectionAttemptIntervals = timeInterval / maxAttemptRate;
    375 
    376         mWifiConnectivityManager.handleScreenStateChanged(false);
    377 
    378         // First attempt the max rate number of connections within the rate interval.
    379         long currentTimeStamp = 0;
    380         for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
    381             currentTimeStamp += connectionAttemptIntervals;
    382             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    383             // Set WiFi to disconnected state to trigger PNO scan
    384             mWifiConnectivityManager.handleConnectionStateChanged(
    385                     WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    386             numAttempts++;
    387         }
    388         // Now trigger another connection attempt after the rate interval, this should not be
    389         // skipped because we should've evicted the older attempt.
    390         when(mClock.elapsedRealtime()).thenReturn(
    391                 currentTimeStamp + connectionAttemptIntervals * 2);
    392         // Set WiFi to disconnected state to trigger PNO scan
    393         mWifiConnectivityManager.handleConnectionStateChanged(
    394                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    395         numAttempts++;
    396 
    397         // Verify that all the connection attempts went through
    398         verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
    399                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    400     }
    401 
    402     /**
    403      * Multiple back to back connection attempts after a user selection should not be rate limited.
    404      *
    405      * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
    406      * with the expected candidate network ID and BSSID for only the expected number of times within
    407      * the given interval.
    408      */
    409     @Test
    410     public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() {
    411         int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
    412         int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
    413         int numAttempts = 0;
    414         int connectionAttemptIntervals = timeInterval / maxAttemptRate;
    415 
    416         mWifiConnectivityManager.handleScreenStateChanged(false);
    417 
    418         // First attempt the max rate number of connections within the rate interval.
    419         long currentTimeStamp = 0;
    420         for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
    421             currentTimeStamp += connectionAttemptIntervals;
    422             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    423             // Set WiFi to disconnected state to trigger PNO scan
    424             mWifiConnectivityManager.handleConnectionStateChanged(
    425                     WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    426             numAttempts++;
    427         }
    428 
    429         mWifiConnectivityManager.connectToUserSelectNetwork(CANDIDATE_NETWORK_ID, false);
    430 
    431         for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
    432             currentTimeStamp += connectionAttemptIntervals;
    433             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    434             // Set WiFi to disconnected state to trigger PNO scan
    435             mWifiConnectivityManager.handleConnectionStateChanged(
    436                     WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    437             numAttempts++;
    438         }
    439 
    440         // Verify that all the connection attempts went through
    441         verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
    442                 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
    443     }
    444 
    445     /**
    446      *  PNO retry for low RSSI networks.
    447      *
    448      * Expected behavior: WifiConnectivityManager doubles the low RSSI
    449      * network retry delay value after QNS skips the PNO scan results
    450      * because of their low RSSI values.
    451      */
    452     @Test
    453     public void PnoRetryForLowRssiNetwork() {
    454         when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
    455               anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
    456 
    457         // Set screen to off
    458         mWifiConnectivityManager.handleScreenStateChanged(false);
    459 
    460         // Get the current retry delay value
    461         int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager
    462                 .getLowRssiNetworkRetryDelay();
    463 
    464         // Set WiFi to disconnected state to trigger PNO scan
    465         mWifiConnectivityManager.handleConnectionStateChanged(
    466                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    467 
    468         // Get the retry delay value after QNS didn't select a
    469         // network candicate from the PNO scan results.
    470         int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager
    471                 .getLowRssiNetworkRetryDelay();
    472 
    473         assertEquals(lowRssiNetworkRetryDelayStartValue * 2,
    474             lowRssiNetworkRetryDelayAfterPnoValue);
    475     }
    476 
    477     /**
    478      * Ensure that the watchdog bite increments the "Pno bad" metric.
    479      *
    480      * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
    481      * a candidate while watchdog single scan did.
    482      */
    483     @Test
    484     public void watchdogBitePnoBadIncrementsMetrics() {
    485         // Set screen to off
    486         mWifiConnectivityManager.handleScreenStateChanged(false);
    487 
    488         // Set WiFi to disconnected state to trigger PNO scan
    489         mWifiConnectivityManager.handleConnectionStateChanged(
    490                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    491 
    492         // Now fire the watchdog alarm and verify the metrics were incremented.
    493         mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
    494         mLooper.dispatchAll();
    495 
    496         verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad();
    497         verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood();
    498     }
    499 
    500     /**
    501      * Ensure that the watchdog bite increments the "Pno good" metric.
    502      *
    503      * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
    504      * a candidate which was the same with watchdog single scan.
    505      */
    506     @Test
    507     public void watchdogBitePnoGoodIncrementsMetrics() {
    508         // Qns returns no candidate after watchdog single scan.
    509         when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
    510                 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
    511 
    512         // Set screen to off
    513         mWifiConnectivityManager.handleScreenStateChanged(false);
    514 
    515         // Set WiFi to disconnected state to trigger PNO scan
    516         mWifiConnectivityManager.handleConnectionStateChanged(
    517                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    518 
    519         // Now fire the watchdog alarm and verify the metrics were incremented.
    520         mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
    521         mLooper.dispatchAll();
    522 
    523         verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood();
    524         verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad();
    525     }
    526 
    527     /**
    528      *  Verify that scan interval for screen on and wifi disconnected scenario
    529      *  is in the exponential backoff fashion.
    530      *
    531      * Expected behavior: WifiConnectivityManager doubles periodic
    532      * scan interval.
    533      */
    534     @Test
    535     public void checkPeriodicScanIntervalWhenDisconnected() {
    536         long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
    537         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    538 
    539         // Set screen to ON
    540         mWifiConnectivityManager.handleScreenStateChanged(true);
    541 
    542         // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
    543         // by screen state change can settle
    544         currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
    545         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    546 
    547         // Set WiFi to disconnected state to trigger periodic scan
    548         mWifiConnectivityManager.handleConnectionStateChanged(
    549                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    550 
    551         // Get the first periodic scan interval
    552         long firstIntervalMs = mAlarmManager
    553                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    554                     - currentTimeStamp;
    555         assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
    556 
    557         currentTimeStamp += firstIntervalMs;
    558         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    559 
    560         // Now fire the first periodic scan alarm timer
    561         mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
    562         mLooper.dispatchAll();
    563 
    564         // Get the second periodic scan interval
    565         long secondIntervalMs = mAlarmManager
    566                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    567                     - currentTimeStamp;
    568 
    569         // Verify the intervals are exponential back off
    570         assertEquals(firstIntervalMs * 2, secondIntervalMs);
    571 
    572         currentTimeStamp += secondIntervalMs;
    573         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    574 
    575         // Make sure we eventually stay at the maximum scan interval.
    576         long intervalMs = 0;
    577         for (int i = 0; i < 5; i++) {
    578             mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
    579             mLooper.dispatchAll();
    580             intervalMs = mAlarmManager
    581                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    582                     - currentTimeStamp;
    583             currentTimeStamp += intervalMs;
    584             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    585         }
    586 
    587         assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
    588     }
    589 
    590     /**
    591      *  Verify that scan interval for screen on and wifi connected scenario
    592      *  is in the exponential backoff fashion.
    593      *
    594      * Expected behavior: WifiConnectivityManager doubles periodic
    595      * scan interval.
    596      */
    597     @Test
    598     public void checkPeriodicScanIntervalWhenConnected() {
    599         long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
    600         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    601 
    602         // Set screen to ON
    603         mWifiConnectivityManager.handleScreenStateChanged(true);
    604 
    605         // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
    606         // by screen state change can settle
    607         currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
    608         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    609 
    610         // Set WiFi to connected state to trigger periodic scan
    611         mWifiConnectivityManager.handleConnectionStateChanged(
    612                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    613 
    614         // Get the first periodic scan interval
    615         long firstIntervalMs = mAlarmManager
    616                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    617                     - currentTimeStamp;
    618         assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
    619 
    620         currentTimeStamp += firstIntervalMs;
    621         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    622 
    623         // Now fire the first periodic scan alarm timer
    624         mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
    625         mLooper.dispatchAll();
    626 
    627         // Get the second periodic scan interval
    628         long secondIntervalMs = mAlarmManager
    629                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    630                     - currentTimeStamp;
    631 
    632         // Verify the intervals are exponential back off
    633         assertEquals(firstIntervalMs * 2, secondIntervalMs);
    634 
    635         currentTimeStamp += secondIntervalMs;
    636         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    637 
    638         // Make sure we eventually stay at the maximum scan interval.
    639         long intervalMs = 0;
    640         for (int i = 0; i < 5; i++) {
    641             mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
    642             mLooper.dispatchAll();
    643             intervalMs = mAlarmManager
    644                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
    645                     - currentTimeStamp;
    646             currentTimeStamp += intervalMs;
    647             when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    648         }
    649 
    650         assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
    651     }
    652 
    653     /**
    654      *  When screen on trigger two connection state change events back to back to
    655      *  verify that the minium scan interval is enforced.
    656      *
    657      * Expected behavior: WifiConnectivityManager start the second periodic single
    658      * scan PERIODIC_SCAN_INTERVAL_MS after the first one.
    659      */
    660     @Test
    661     public void checkMinimumPeriodicScanIntervalWhenScreenOn() {
    662         long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
    663         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    664 
    665         // Set screen to ON
    666         mWifiConnectivityManager.handleScreenStateChanged(true);
    667 
    668         // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
    669         // by screen state change can settle
    670         currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
    671         long firstScanTimeStamp = currentTimeStamp;
    672         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    673 
    674         // Set WiFi to connected state to trigger the periodic scan
    675         mWifiConnectivityManager.handleConnectionStateChanged(
    676                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    677 
    678         // Set the second scan attempt time stamp.
    679         currentTimeStamp += 2000;
    680         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    681 
    682         // Set WiFi to disconnected state to trigger another periodic scan
    683         mWifiConnectivityManager.handleConnectionStateChanged(
    684                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
    685 
    686         // Get the second periodic scan actual time stamp
    687         long secondScanTimeStamp = mAlarmManager
    688                     .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
    689 
    690         // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after the
    691         // very first scan.
    692         assertEquals(secondScanTimeStamp, firstScanTimeStamp
    693                        + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
    694 
    695     }
    696 
    697     /**
    698      *  When screen on trigger a connection state change event and a forced connectivity
    699      *  scan event back to back to verify that the minimum scan interval is not applied
    700      *  in this scenario.
    701      *
    702      * Expected behavior: WifiConnectivityManager starts the second periodic single
    703      * scan immediately.
    704      */
    705     @Test
    706     public void checkMinimumPeriodicScanIntervalNotEnforced() {
    707         long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
    708         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    709 
    710         // Set screen to ON
    711         mWifiConnectivityManager.handleScreenStateChanged(true);
    712 
    713         // Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
    714         // by screen state change can settle
    715         currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
    716         long firstScanTimeStamp = currentTimeStamp;
    717         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    718 
    719         // Set WiFi to connected state to trigger the periodic scan
    720         mWifiConnectivityManager.handleConnectionStateChanged(
    721                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    722 
    723         // Set the second scan attempt time stamp
    724         currentTimeStamp += 2000;
    725         when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
    726 
    727         // Force a connectivity scan
    728         mWifiConnectivityManager.forceConnectivityScan();
    729 
    730         // Get the second periodic scan actual time stamp. Note, this scan is not
    731         // started from the AlarmManager.
    732         long secondScanTimeStamp = mWifiConnectivityManager.getLastPeriodicSingleScanTimeStamp();
    733 
    734         // Verify that the second scan is fired immediately
    735         assertEquals(secondScanTimeStamp, currentTimeStamp);
    736     }
    737 
    738     /**
    739      * Verify that we perform full band scan when the currently connected network's tx/rx success
    740      * rate is low.
    741      *
    742      * Expected behavior: WifiConnectivityManager does full band scan.
    743      */
    744     @Test
    745     public void checkSingleScanSettingsWhenConnectedWithLowDataRate() {
    746         mWifiInfo.txSuccessRate = 0;
    747         mWifiInfo.rxSuccessRate = 0;
    748 
    749         final HashSet<Integer> channelList = new HashSet<>();
    750         channelList.add(1);
    751         channelList.add(2);
    752         channelList.add(3);
    753 
    754         when(mWifiStateMachine.getCurrentWifiConfiguration())
    755                 .thenReturn(new WifiConfiguration());
    756         when(mWifiStateMachine.getFrequencyBand())
    757                 .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ);
    758         when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
    759                 .thenReturn(channelList);
    760 
    761         doAnswer(new AnswerWithArguments() {
    762             public void answer(ScanSettings settings, ScanListener listener,
    763                     WorkSource workSource) throws Exception {
    764                 assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS);
    765                 assertNull(settings.channels);
    766             }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    767 
    768         // Set screen to ON
    769         mWifiConnectivityManager.handleScreenStateChanged(true);
    770 
    771         // Set WiFi to connected state to trigger periodic scan
    772         mWifiConnectivityManager.handleConnectionStateChanged(
    773                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    774 
    775         verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    776     }
    777 
    778     /**
    779      * Verify that we perform partial scan when the currently connected network's tx/rx success
    780      * rate is high and when the currently connected network is present in scan
    781      * cache in WifiConfigManager.
    782      *
    783      * Expected behavior: WifiConnectivityManager does full band scan.
    784      */
    785     @Test
    786     public void checkSingleScanSettingsWhenConnectedWithHighDataRate() {
    787         mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
    788         mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
    789 
    790         final HashSet<Integer> channelList = new HashSet<>();
    791         channelList.add(1);
    792         channelList.add(2);
    793         channelList.add(3);
    794 
    795         when(mWifiStateMachine.getCurrentWifiConfiguration())
    796                 .thenReturn(new WifiConfiguration());
    797         when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
    798                 .thenReturn(channelList);
    799 
    800         doAnswer(new AnswerWithArguments() {
    801             public void answer(ScanSettings settings, ScanListener listener,
    802                     WorkSource workSource) throws Exception {
    803                 assertEquals(settings.band, WifiScanner.WIFI_BAND_UNSPECIFIED);
    804                 assertEquals(settings.channels.length, channelList.size());
    805                 for (int chanIdx = 0; chanIdx < settings.channels.length; chanIdx++) {
    806                     assertTrue(channelList.contains(settings.channels[chanIdx].frequency));
    807                 }
    808             }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    809 
    810         // Set screen to ON
    811         mWifiConnectivityManager.handleScreenStateChanged(true);
    812 
    813         // Set WiFi to connected state to trigger periodic scan
    814         mWifiConnectivityManager.handleConnectionStateChanged(
    815                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    816 
    817         verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    818     }
    819 
    820     /**
    821      * Verify that we fall back to full band scan when the currently connected network's tx/rx
    822      * success rate is high and the currently connected network is not present in scan cache in
    823      * WifiConfigManager. This is simulated by returning an empty hashset in |makeChannelList|.
    824      *
    825      * Expected behavior: WifiConnectivityManager does full band scan.
    826      */
    827     @Test
    828     public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() {
    829         mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
    830         mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
    831 
    832         final HashSet<Integer> channelList = new HashSet<>();
    833 
    834         when(mWifiStateMachine.getCurrentWifiConfiguration())
    835                 .thenReturn(new WifiConfiguration());
    836         when(mWifiStateMachine.getFrequencyBand())
    837                 .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ);
    838         when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
    839                 .thenReturn(channelList);
    840 
    841         doAnswer(new AnswerWithArguments() {
    842             public void answer(ScanSettings settings, ScanListener listener,
    843                     WorkSource workSource) throws Exception {
    844                 assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS);
    845                 assertNull(settings.channels);
    846             }}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    847 
    848         // Set screen to ON
    849         mWifiConnectivityManager.handleScreenStateChanged(true);
    850 
    851         // Set WiFi to connected state to trigger periodic scan
    852         mWifiConnectivityManager.handleConnectionStateChanged(
    853                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
    854 
    855         verify(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
    856     }
    857 }
    858