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