WifiConnectivityManagerTest.java revision 50abba06efa7834b5309df561375e4a2e2df630d
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; 20 21import static org.mockito.Mockito.*; 22 23import android.app.AlarmManager; 24import android.content.Context; 25import android.content.res.Resources; 26import android.net.wifi.ScanResult; 27import android.net.wifi.ScanResult.InformationElement; 28import android.net.wifi.SupplicantState; 29import android.net.wifi.WifiConfiguration; 30import android.net.wifi.WifiInfo; 31import android.net.wifi.WifiScanner; 32import android.net.wifi.WifiSsid; 33import android.net.wifi.WifiScanner.PnoScanListener; 34import android.net.wifi.WifiScanner.PnoSettings; 35import android.net.wifi.WifiScanner.ScanListener; 36import android.net.wifi.WifiScanner.ScanSettings; 37import android.os.Looper; 38import android.os.WorkSource; 39import android.test.suitebuilder.annotation.SmallTest; 40 41import com.android.internal.R; 42import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; 43import com.android.server.wifi.util.InformationElementUtil; 44 45import org.junit.After; 46import org.junit.Before; 47import org.junit.Test; 48 49import java.nio.charset.StandardCharsets; 50import java.util.ArrayList; 51import java.util.concurrent.atomic.AtomicInteger; 52 53/** 54 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}. 55 */ 56@SmallTest 57public class WifiConnectivityManagerTest { 58 59 /** 60 * Called before each test 61 */ 62 @Before 63 public void setUp() throws Exception { 64 mWifiInjector = mockWifiInjector(); 65 mResource = mockResource(); 66 mAlarmManager = mockAlarmManager(); 67 mContext = mockContext(); 68 mWifiStateMachine = mockWifiStateMachine(); 69 mWifiConfigManager = mockWifiConfigManager(); 70 mWifiInfo = mockWifiInfo(); 71 mWifiScanner = mockWifiScanner(); 72 mWifiQNS = mockWifiQualifiedNetworkSelector(); 73 mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine, 74 mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector, 75 mLooper.getLooper()); 76 mWifiConnectivityManager.setWifiEnabled(true); 77 when(mClock.currentTimeMillis()).thenReturn(System.currentTimeMillis()); 78 } 79 80 /** 81 * Called after each test 82 */ 83 @After 84 public void cleanup() { 85 validateMockitoUsage(); 86 } 87 88 private Resources mResource; 89 private Context mContext; 90 private AlarmManager mAlarmManager; 91 private MockLooper mLooper = new MockLooper(); 92 private WifiConnectivityManager mWifiConnectivityManager; 93 private WifiQualifiedNetworkSelector mWifiQNS; 94 private WifiStateMachine mWifiStateMachine; 95 private WifiScanner mWifiScanner; 96 private WifiConfigManager mWifiConfigManager; 97 private WifiInfo mWifiInfo; 98 private Clock mClock = mock(Clock.class); 99 private WifiLastResortWatchdog mWifiLastResortWatchdog; 100 private WifiInjector mWifiInjector; 101 102 private static final int CANDIDATE_NETWORK_ID = 0; 103 private static final String CANDIDATE_SSID = "\"AnSsid\""; 104 private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3"; 105 private static final String TAG = "WifiConnectivityManager Unit Test"; 106 107 Resources mockResource() { 108 Resources resource = mock(Resources.class); 109 110 when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80); 111 when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24); 112 113 return resource; 114 } 115 116 AlarmManager mockAlarmManager() { 117 AlarmManager alarmManager = mock(AlarmManager.class); 118 119 return alarmManager; 120 } 121 122 Context mockContext() { 123 Context context = mock(Context.class); 124 125 when(context.getResources()).thenReturn(mResource); 126 when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( 127 mAlarmManager); 128 129 return context; 130 } 131 132 WifiScanner mockWifiScanner() { 133 WifiScanner scanner = mock(WifiScanner.class); 134 135 // dummy scan results. QNS PeriodicScanListener bulids scanDetails from 136 // the fullScanResult and doesn't really use results 137 final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1]; 138 139 // do a synchronous answer for the ScanListener callbacks 140 doAnswer(new AnswerWithArguments() { 141 public void answer(ScanSettings settings, ScanListener listener, 142 WorkSource workSource) throws Exception { 143 listener.onResults(scanDatas); 144 }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject()); 145 146 doAnswer(new AnswerWithArguments() { 147 public void answer(ScanSettings settings, ScanListener listener, 148 WorkSource workSource) throws Exception { 149 listener.onResults(scanDatas); 150 }}).when(scanner).startScan(anyObject(), anyObject(), anyObject()); 151 152 // This unfortunately needs to be a somewhat valid scan result, otherwise 153 // |ScanDetailUtil.toScanDetail| raises exceptions. 154 final ScanResult[] scanResults = new ScanResult[1]; 155 scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID), 156 CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", 157 -78, 2450, 1025, 22, 33, 20, 0, 0, true); 158 scanResults[0].informationElements = new InformationElement[1]; 159 scanResults[0].informationElements[0] = new InformationElement(); 160 scanResults[0].informationElements[0].id = InformationElement.EID_SSID; 161 scanResults[0].informationElements[0].bytes = 162 CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8); 163 164 doAnswer(new AnswerWithArguments() { 165 public void answer(ScanSettings settings, PnoSettings pnoSettings, 166 PnoScanListener listener) throws Exception { 167 listener.onPnoNetworkFound(scanResults); 168 }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject()); 169 170 doAnswer(new AnswerWithArguments() { 171 public void answer(ScanSettings settings, PnoSettings pnoSettings, 172 PnoScanListener listener) throws Exception { 173 listener.onPnoNetworkFound(scanResults); 174 }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject()); 175 176 return scanner; 177 } 178 179 WifiStateMachine mockWifiStateMachine() { 180 WifiStateMachine stateMachine = mock(WifiStateMachine.class); 181 182 when(stateMachine.getFrequencyBand()).thenReturn(1); 183 when(stateMachine.isLinkDebouncing()).thenReturn(false); 184 when(stateMachine.isConnected()).thenReturn(false); 185 when(stateMachine.isDisconnected()).thenReturn(true); 186 when(stateMachine.isSupplicantTransientState()).thenReturn(false); 187 188 return stateMachine; 189 } 190 191 WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() { 192 WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class); 193 194 WifiConfiguration candidate = generateWifiConfig( 195 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null); 196 candidate.BSSID = CANDIDATE_BSSID; 197 ScanResult candidateScanResult = new ScanResult(); 198 candidateScanResult.SSID = CANDIDATE_SSID; 199 candidateScanResult.BSSID = CANDIDATE_BSSID; 200 candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult); 201 202 when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(), 203 anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate); 204 return qns; 205 } 206 207 WifiInfo mockWifiInfo() { 208 WifiInfo wifiInfo = mock(WifiInfo.class); 209 210 when(wifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID); 211 when(wifiInfo.getBSSID()).thenReturn(null); 212 when(wifiInfo.getSupplicantState()).thenReturn(SupplicantState.DISCONNECTED); 213 214 return wifiInfo; 215 } 216 217 WifiConfigManager mockWifiConfigManager() { 218 WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class); 219 220 when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null); 221 wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger( 222 WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND); 223 wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger( 224 WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD); 225 226 // Pass dummy pno network list, otherwise Pno scan requests will not be triggered. 227 PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID); 228 ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>(); 229 pnoNetworkList.add(pnoNetwork); 230 when(wifiConfigManager.retrieveDisconnectedPnoNetworkList()).thenReturn(pnoNetworkList); 231 when(wifiConfigManager.retrieveConnectedPnoNetworkList()).thenReturn(pnoNetworkList); 232 233 return wifiConfigManager; 234 } 235 236 WifiInjector mockWifiInjector() { 237 WifiInjector wifiInjector = mock(WifiInjector.class); 238 mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class); 239 when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); 240 when(wifiInjector.getClock()).thenReturn(mClock); 241 return wifiInjector; 242 } 243 244 /** 245 * Wifi enters disconnected state while screen is on. 246 * 247 * Expected behavior: WifiConnectivityManager calls 248 * WifiStateMachine.autoConnectToNetwork() with the 249 * expected candidate network ID and BSSID. 250 */ 251 @Test 252 public void enterWifiDisconnectedStateWhenScreenOn() { 253 // Set screen to on 254 mWifiConnectivityManager.handleScreenStateChanged(true); 255 256 // Set WiFi to disconnected state 257 mWifiConnectivityManager.handleConnectionStateChanged( 258 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 259 260 verify(mWifiStateMachine).autoConnectToNetwork( 261 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 262 } 263 264 /** 265 * Wifi enters connected state while screen is on. 266 * 267 * Expected behavior: WifiConnectivityManager calls 268 * WifiStateMachine.autoConnectToNetwork() with the 269 * expected candidate network ID and BSSID. 270 */ 271 @Test 272 public void enterWifiConnectedStateWhenScreenOn() { 273 // Set screen to on 274 mWifiConnectivityManager.handleScreenStateChanged(true); 275 276 // Set WiFi to connected state 277 mWifiConnectivityManager.handleConnectionStateChanged( 278 WifiConnectivityManager.WIFI_STATE_CONNECTED); 279 280 verify(mWifiStateMachine).autoConnectToNetwork( 281 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 282 } 283 284 /** 285 * Screen turned on while WiFi in disconnected state. 286 * 287 * Expected behavior: WifiConnectivityManager calls 288 * WifiStateMachine.autoConnectToNetwork() with the 289 * expected candidate network ID and BSSID. 290 */ 291 @Test 292 public void turnScreenOnWhenWifiInDisconnectedState() { 293 // Set WiFi to disconnected state 294 mWifiConnectivityManager.handleConnectionStateChanged( 295 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 296 297 // Set screen to on 298 mWifiConnectivityManager.handleScreenStateChanged(true); 299 300 verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork( 301 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 302 } 303 304 /** 305 * Screen turned on while WiFi in connected state. 306 * 307 * Expected behavior: WifiConnectivityManager calls 308 * WifiStateMachine.autoConnectToNetwork() with the 309 * expected candidate network ID and BSSID. 310 */ 311 @Test 312 public void turnScreenOnWhenWifiInConnectedState() { 313 // Set WiFi to connected state 314 mWifiConnectivityManager.handleConnectionStateChanged( 315 WifiConnectivityManager.WIFI_STATE_CONNECTED); 316 317 // Set screen to on 318 mWifiConnectivityManager.handleScreenStateChanged(true); 319 320 verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork( 321 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 322 } 323 324 /** 325 * Multiple back to back connection attempts within the rate interval should be rate limited. 326 * 327 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 328 * with the expected candidate network ID and BSSID for only the expected number of times within 329 * the given interval. 330 */ 331 @Test 332 public void connectionAttemptRateLimitedWhenScreenOff() { 333 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 334 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 335 int numAttempts = 0; 336 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 337 338 mWifiConnectivityManager.handleScreenStateChanged(false); 339 340 // First attempt the max rate number of connections within the rate interval. 341 long currentTimeStamp = 0; 342 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 343 currentTimeStamp += connectionAttemptIntervals; 344 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 345 // Set WiFi to disconnected state to trigger PNO scan 346 mWifiConnectivityManager.handleConnectionStateChanged( 347 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 348 numAttempts++; 349 } 350 // Now trigger another connection attempt before the rate interval, this should be 351 // skipped because we've crossed rate limit. 352 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 353 // Set WiFi to disconnected state to trigger PNO scan 354 mWifiConnectivityManager.handleConnectionStateChanged( 355 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 356 357 // Verify that we attempt to connect upto the rate. 358 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 359 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 360 } 361 362 /** 363 * Multiple back to back connection attempts outside the rate interval should not be rate 364 * limited. 365 * 366 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 367 * with the expected candidate network ID and BSSID for only the expected number of times within 368 * the given interval. 369 */ 370 @Test 371 public void connectionAttemptNotRateLimitedWhenScreenOff() { 372 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 373 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 374 int numAttempts = 0; 375 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 376 377 mWifiConnectivityManager.handleScreenStateChanged(false); 378 379 // First attempt the max rate number of connections within the rate interval. 380 long currentTimeStamp = 0; 381 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 382 currentTimeStamp += connectionAttemptIntervals; 383 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 384 // Set WiFi to disconnected state to trigger PNO scan 385 mWifiConnectivityManager.handleConnectionStateChanged( 386 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 387 numAttempts++; 388 } 389 // Now trigger another connection attempt after the rate interval, this should not be 390 // skipped because we should've evicted the older attempt. 391 when(mClock.currentTimeMillis()).thenReturn( 392 currentTimeStamp + connectionAttemptIntervals * 2); 393 // Set WiFi to disconnected state to trigger PNO scan 394 mWifiConnectivityManager.handleConnectionStateChanged( 395 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 396 numAttempts++; 397 398 // Verify that all the connection attempts went through 399 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 400 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 401 } 402 403 /** 404 * Multiple back to back connection attempts after a user selection should not be rate limited. 405 * 406 * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork() 407 * with the expected candidate network ID and BSSID for only the expected number of times within 408 * the given interval. 409 */ 410 @Test 411 public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() { 412 int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE; 413 int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS; 414 int numAttempts = 0; 415 int connectionAttemptIntervals = timeInterval / maxAttemptRate; 416 417 mWifiConnectivityManager.handleScreenStateChanged(false); 418 419 // First attempt the max rate number of connections within the rate interval. 420 long currentTimeStamp = 0; 421 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 422 currentTimeStamp += connectionAttemptIntervals; 423 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 424 // Set WiFi to disconnected state to trigger PNO scan 425 mWifiConnectivityManager.handleConnectionStateChanged( 426 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 427 numAttempts++; 428 } 429 430 mWifiConnectivityManager.connectToUserSelectNetwork(CANDIDATE_NETWORK_ID, false); 431 432 for (int attempt = 0; attempt < maxAttemptRate; attempt++) { 433 currentTimeStamp += connectionAttemptIntervals; 434 when(mClock.currentTimeMillis()).thenReturn(currentTimeStamp); 435 // Set WiFi to disconnected state to trigger PNO scan 436 mWifiConnectivityManager.handleConnectionStateChanged( 437 WifiConnectivityManager.WIFI_STATE_DISCONNECTED); 438 numAttempts++; 439 } 440 441 // Verify that all the connection attempts went through 442 verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork( 443 CANDIDATE_NETWORK_ID, CANDIDATE_BSSID); 444 } 445} 446