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