WifiStateMachineTest.java revision 772124d1f1ddb2b9537de5efc748943808dafe80
1/* 2 * Copyright (C) 2015 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 org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertTrue; 21import static org.mockito.Mockito.any; 22import static org.mockito.Mockito.anyBoolean; 23import static org.mockito.Mockito.anyInt; 24import static org.mockito.Mockito.anyObject; 25import static org.mockito.Mockito.anyString; 26import static org.mockito.Mockito.mock; 27import static org.mockito.Mockito.verify; 28import static org.mockito.Mockito.when; 29import static org.mockito.Mockito.withSettings; 30 31import android.content.ContentResolver; 32import android.content.Context; 33import android.content.pm.PackageManager; 34import android.content.res.Resources; 35import android.net.BaseDhcpStateMachine; 36import android.net.ConnectivityManager; 37import android.net.DhcpResults; 38import android.net.DhcpStateMachine; 39import android.net.ip.IpReachabilityMonitor; 40import android.net.wifi.ScanResult; 41import android.net.wifi.SupplicantState; 42import android.net.wifi.WifiConfiguration; 43import android.net.wifi.WifiManager; 44import android.net.wifi.WifiScanner; 45import android.net.wifi.WifiSsid; 46import android.net.wifi.p2p.IWifiP2pManager; 47import android.os.BatteryStats; 48import android.os.Handler; 49import android.os.HandlerThread; 50import android.os.IBinder; 51import android.os.IInterface; 52import android.os.INetworkManagementService; 53import android.os.IPowerManager; 54import android.os.Looper; 55import android.os.Message; 56import android.os.Messenger; 57import android.os.PowerManager; 58import android.provider.Settings; 59import android.test.suitebuilder.annotation.SmallTest; 60import android.util.Log; 61 62import com.android.internal.R; 63import com.android.internal.app.IBatteryStats; 64import com.android.internal.util.AsyncChannel; 65import com.android.internal.util.IState; 66import com.android.internal.util.StateMachine; 67import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments; 68import com.android.server.wifi.hotspot2.NetworkDetail; 69import com.android.server.wifi.hotspot2.SupplicantBridge; 70import com.android.server.wifi.hotspot2.omadm.MOManager; 71import com.android.server.wifi.hotspot2.osu.OSUManager; 72import com.android.server.wifi.p2p.WifiP2pServiceImpl; 73 74import org.junit.After; 75import org.junit.Before; 76import org.junit.Test; 77import org.mockito.ArgumentCaptor; 78import org.mockito.Mock; 79import org.mockito.Mockito; 80import org.mockito.MockitoAnnotations; 81 82import java.io.ByteArrayOutputStream; 83import java.io.PrintWriter; 84import java.lang.reflect.Field; 85import java.lang.reflect.InvocationTargetException; 86import java.lang.reflect.Method; 87import java.util.ArrayList; 88import java.util.Arrays; 89import java.util.HashMap; 90import java.util.List; 91import java.util.Map; 92 93/** 94 * Unit tests for {@link com.android.server.wifi.WifiStateMachine}. 95 */ 96@SmallTest 97public class WifiStateMachineTest { 98 public static final String TAG = "WifiStateMachineTest"; 99 100 private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) { 101 return mock(class1, withSettings().extraInterfaces(interfaces)); 102 } 103 104 private static <T, I> IBinder mockService(Class<T> class1, Class<I> iface) { 105 T tImpl = mockWithInterfaces(class1, iface); 106 IBinder binder = mock(IBinder.class); 107 when(((IInterface) tImpl).asBinder()).thenReturn(binder); 108 when(binder.queryLocalInterface(iface.getCanonicalName())) 109 .thenReturn((IInterface) tImpl); 110 return binder; 111 } 112 113 private void enableDebugLogs() { 114 mWsm.enableVerboseLogging(1); 115 } 116 117 private static void installWlanWifiNative(WifiNative wifiNative) throws Exception { 118 Field field = WifiNative.class.getDeclaredField("wlanNativeInterface"); 119 field.setAccessible(true); 120 field.set(null, wifiNative); 121 122 when(wifiNative.getInterfaceName()).thenReturn("wlan0"); 123 } 124 125 private FrameworkFacade getFrameworkFacade() throws InterruptedException { 126 FrameworkFacade facade = mock(FrameworkFacade.class); 127 128 when(facade.makeBaseLogger()).thenReturn(mock(BaseWifiLogger.class)); 129 when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn( 130 mockWithInterfaces(IBinder.class, INetworkManagementService.class)); 131 132 IBinder p2pBinder = mockService(WifiP2pServiceImpl.class, IWifiP2pManager.class); 133 when(facade.getService(Context.WIFI_P2P_SERVICE)).thenReturn(p2pBinder); 134 135 WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface( 136 IWifiP2pManager.class.getCanonicalName()); 137 138 final Object sync = new Object(); 139 synchronized (sync) { 140 mP2pThread = new HandlerThread("WifiP2pMockThread") { 141 @Override 142 protected void onLooperPrepared() { 143 synchronized (sync) { 144 sync.notifyAll(); 145 } 146 } 147 }; 148 149 mP2pThread.start(); 150 sync.wait(); 151 } 152 153 Handler handler = new Handler(mP2pThread.getLooper()); 154 when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler)); 155 156 IBinder batteryStatsBinder = mockService(BatteryStats.class, IBatteryStats.class); 157 when(facade.getService(BatteryStats.SERVICE_NAME)).thenReturn(batteryStatsBinder); 158 159 when(facade.makeOsuManager(any(WifiConfigStore.class), any(Context.class), any( 160 SupplicantBridge.class), any(MOManager.class), any(WifiStateMachine.class))) 161 .thenReturn(mock(OSUManager.class)); 162 163 when(facade.makeDhcpStateMachine( 164 any(Context.class), any(StateMachine.class), any(String.class))).thenReturn( 165 mock(BaseDhcpStateMachine.class)); 166 167 when(facade.makeIpReachabilityMonitor(any(Context.class), anyString(), 168 any(IpReachabilityMonitor.Callback.class))).thenReturn( 169 mock(IpReachabilityMonitor.class)); 170 171 return facade; 172 } 173 174 private Context getContext() throws Exception { 175 PackageManager pkgMgr = mock(PackageManager.class); 176 when(pkgMgr.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true); 177 178 Context context = mock(Context.class); 179 when(context.getPackageManager()).thenReturn(pkgMgr); 180 when(context.getContentResolver()).thenReturn(mock(ContentResolver.class)); 181 182 MockResources resources = new com.android.server.wifi.MockResources(); 183 when(context.getResources()).thenReturn(resources); 184 185 ContentResolver cr = mock(ContentResolver.class); 186 when(context.getContentResolver()).thenReturn(cr); 187 188 when(context.getSystemService(Context.POWER_SERVICE)).thenReturn( 189 new PowerManager(context, mock(IPowerManager.class), new Handler())); 190 191 mAlarmManager = new MockAlarmManager(); 192 when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( 193 mAlarmManager.getAlarmManager()); 194 195 when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( 196 mock(ConnectivityManager.class)); 197 198 when(context.getSystemService(Context.WIFI_SCANNING_SERVICE)).thenReturn(mWifiScanner); 199 200 return context; 201 } 202 203 private Resources getMockResources() { 204 MockResources resources = new MockResources(); 205 resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false); 206 return resources; 207 } 208 209 private IState getCurrentState() throws 210 NoSuchMethodException, InvocationTargetException, IllegalAccessException { 211 Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); 212 method.setAccessible(true); 213 return (IState) method.invoke(mWsm); 214 } 215 216 private static HandlerThread getWsmHandlerThread(WifiStateMachine wsm) throws 217 NoSuchFieldException, InvocationTargetException, IllegalAccessException { 218 Field field = StateMachine.class.getDeclaredField("mSmThread"); 219 field.setAccessible(true); 220 return (HandlerThread) field.get(wsm); 221 } 222 223 private static void stopLooper(final Looper looper) throws Exception { 224 new Handler(looper).post(new Runnable() { 225 @Override 226 public void run() { 227 looper.quitSafely(); 228 } 229 }); 230 } 231 232 private void wait(int delayInMs) throws InterruptedException { 233 Looper looper = mWsmThread.getLooper(); 234 final Handler handler = new Handler(looper); 235 synchronized (handler) { 236 handler.postDelayed(new Runnable() { 237 @Override 238 public void run() { 239 synchronized (handler) { 240 handler.notifyAll(); 241 } 242 } 243 }, delayInMs); 244 245 handler.wait(); 246 } 247 } 248 249 private void dumpState() { 250 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 251 PrintWriter writer = new PrintWriter(stream); 252 mWsm.dump(null, writer, null); 253 writer.flush(); 254 Log.d(TAG, "WifiStateMachine state -" + stream.toString()); 255 } 256 257 private static ScanDetail getGoogleGuestScanDetail(int rssi) { 258 ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1]; 259 ie[0] = ScanResults.generateSsidIe(sSSID); 260 NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq); 261 ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq, 262 Long.MAX_VALUE /* needed so that scan results aren't rejected because 263 there older than scan start */); 264 detail.getScanResult().informationElements = ie; 265 detail.getScanResult().anqpLines = new ArrayList<String>(); 266 return detail; 267 } 268 269 private ScanResult[] getMockScanResults() { 270 ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825); 271 // copy generated results and add space on the end for one more 272 ScanResult[] results = Arrays.copyOf(sr.getScanData().getResults(), 273 sr.getScanData().getResults().length + 1); 274 275 int rssi = -65; 276 results[results.length - 1] = getGoogleGuestScanDetail(rssi).getScanResult(); 277 return results; 278 } 279 280 static final String sSSID = "\"GoogleGuest\""; 281 static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID); 282 static final String sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", ""); 283 static final String sBSSID = "01:02:03:04:05:06"; 284 static final int sFreq = 2437; 285 286 WifiStateMachine mWsm; 287 HandlerThread mWsmThread; 288 HandlerThread mP2pThread; 289 HandlerThread mSyncThread; 290 AsyncChannel mWsmAsyncChannel; 291 MockAlarmManager mAlarmManager; 292 MockWifiMonitor mWifiMonitor; 293 294 @Mock WifiNative mWifiNative; 295 @Mock WifiScanner mWifiScanner; 296 @Mock SupplicantStateTracker mSupplicantStateTracker; 297 @Mock WifiMetrics mWifiMetrics; 298 299 public WifiStateMachineTest() throws Exception { 300 } 301 302 @Before 303 public void setUp() throws Exception { 304 Log.d(TAG, "Setting up ..."); 305 306 // Ensure looper exists 307 MockLooper looper = new MockLooper(); 308 309 MockitoAnnotations.initMocks(this); 310 311 /** uncomment this to enable logs from WifiStateMachines */ 312 // enableDebugLogs(); 313 314 installWlanWifiNative(mWifiNative); 315 mWifiMonitor = new MockWifiMonitor(); 316 mWifiMetrics = mock(WifiMetrics.class); 317 FrameworkFacade factory = getFrameworkFacade(); 318 Context context = getContext(); 319 320 Resources resources = getMockResources(); 321 when(context.getResources()).thenReturn(resources); 322 323 when(factory.getIntegerSetting(context, 324 Settings.Global.WIFI_FREQUENCY_BAND, 325 WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn( 326 WifiManager.WIFI_FREQUENCY_BAND_AUTO); 327 328 when(factory.makeApConfigStore(Mockito.eq(context), any(Handler.class))) 329 .thenCallRealMethod(); 330 331 when(factory.makeSupplicantStateTracker( 332 any(Context.class), any(WifiStateMachine.class), any(WifiConfigStore.class), 333 any(Handler.class))).thenReturn(mSupplicantStateTracker); 334 335 mWsm = new WifiStateMachine(context, null, factory, mWifiMetrics); 336 mWsmThread = getWsmHandlerThread(mWsm); 337 338 final Object sync = new Object(); 339 synchronized (sync) { 340 mSyncThread = new HandlerThread("SynchronizationThread"); 341 final AsyncChannel channel = new AsyncChannel(); 342 mSyncThread.start(); 343 Handler handler = new Handler(mSyncThread.getLooper()) { 344 @Override 345 public void handleMessage(Message msg) { 346 switch (msg.what) { 347 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 348 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 349 mWsmAsyncChannel = channel; 350 synchronized (sync) { 351 sync.notifyAll(); 352 Log.d(TAG, "Successfully connected " + this); 353 } 354 } else { 355 Log.d(TAG, "Failed to connect Command channel " + this); 356 } 357 break; 358 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 359 Log.d(TAG, "Command channel disconnected" + this); 360 break; 361 } 362 } 363 }; 364 365 channel.connect(context, handler, mWsm.getMessenger()); 366 sync.wait(); 367 } 368 369 /* Now channel is supposed to be connected */ 370 } 371 372 @After 373 public void cleanUp() throws Exception { 374 375 if (mSyncThread != null) stopLooper(mSyncThread.getLooper()); 376 if (mWsmThread != null) stopLooper(mWsmThread.getLooper()); 377 if (mP2pThread != null) stopLooper(mP2pThread.getLooper()); 378 379 mWsmThread = null; 380 mP2pThread = null; 381 mSyncThread = null; 382 mWsmAsyncChannel = null; 383 mWsm = null; 384 } 385 386 @Test 387 public void createNew() throws Exception { 388 assertEquals("InitialState", getCurrentState().getName()); 389 390 mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED); 391 wait(200); 392 assertEquals("InitialState", getCurrentState().getName()); 393 } 394 395 @Test 396 public void loadComponents() throws Exception { 397 398 when(mWifiNative.loadDriver()).thenReturn(true); 399 when(mWifiNative.startHal()).thenReturn(true); 400 when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true); 401 402 mWsm.setSupplicantRunning(true); 403 wait(200); 404 assertEquals("SupplicantStartingState", getCurrentState().getName()); 405 406 when(mWifiNative.setBand(anyInt())).thenReturn(true); 407 when(mWifiNative.setDeviceName(anyString())).thenReturn(true); 408 when(mWifiNative.setManufacturer(anyString())).thenReturn(true); 409 when(mWifiNative.setModelName(anyString())).thenReturn(true); 410 when(mWifiNative.setModelNumber(anyString())).thenReturn(true); 411 when(mWifiNative.setSerialNumber(anyString())).thenReturn(true); 412 when(mWifiNative.setConfigMethods(anyString())).thenReturn(true); 413 when(mWifiNative.setDeviceType(anyString())).thenReturn(true); 414 when(mWifiNative.setSerialNumber(anyString())).thenReturn(true); 415 when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true); 416 when(mWifiNative.enableBackgroundScan(anyBoolean())).thenReturn(true); 417 418 mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT); 419 wait(200); 420 assertEquals("DisconnectedState", getCurrentState().getName()); 421 } 422 423 @Test 424 public void loadComponentsFailure() throws Exception { 425 when(mWifiNative.loadDriver()).thenReturn(false); 426 when(mWifiNative.startHal()).thenReturn(false); 427 when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(false); 428 429 mWsm.setSupplicantRunning(true); 430 wait(200); 431 assertEquals("InitialState", getCurrentState().getName()); 432 433 when(mWifiNative.loadDriver()).thenReturn(true); 434 mWsm.setSupplicantRunning(true); 435 wait(200); 436 assertEquals("InitialState", getCurrentState().getName()); 437 438 when(mWifiNative.startHal()).thenReturn(true); 439 mWsm.setSupplicantRunning(true); 440 wait(200); 441 assertEquals("InitialState", getCurrentState().getName()); 442 } 443 444 @Test 445 public void addNetwork() throws Exception { 446 447 loadComponents(); 448 449 final HashMap<String, String> nameToValue = new HashMap<String, String>(); 450 451 when(mWifiNative.addNetwork()).thenReturn(0); 452 when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())) 453 .then(new AnswerWithArguments<Boolean>() { 454 public boolean answer(int netId, String name, String value) { 455 if (netId != 0) { 456 Log.d(TAG, "Can't set var " + name + " for " + netId); 457 return false; 458 } 459 460 Log.d(TAG, "Setting var " + name + " to " + value + " for " + netId); 461 nameToValue.put(name, value); 462 return true; 463 } 464 }); 465 466 when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject())) 467 .then(new AnswerWithArguments<Boolean>() { 468 public boolean answer(int netId, String name, Map<String, String> values) { 469 if (netId != 0) { 470 Log.d(TAG, "Can't set extra " + name + " for " + netId); 471 return false; 472 } 473 474 Log.d(TAG, "Setting extra for " + netId); 475 return true; 476 } 477 }); 478 479 when(mWifiNative.getNetworkVariable(anyInt(), anyString())) 480 .then(new AnswerWithArguments<String>() { 481 public String answer(int netId, String name) throws Throwable { 482 if (netId != 0) { 483 Log.d(TAG, "Can't find var " + name + " for " + netId); 484 return null; 485 } 486 String value = nameToValue.get(name); 487 if (value != null) { 488 Log.d(TAG, "Returning var " + name + " to " + value + " for " + netId); 489 } else { 490 Log.d(TAG, "Can't find var " + name + " for " + netId); 491 } 492 return value; 493 } 494 }); 495 496 WifiConfiguration config = new WifiConfiguration(); 497 config.SSID = sSSID; 498 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 499 mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config); 500 wait(200); 501 502 verify(mWifiNative).addNetwork(); 503 verify(mWifiNative).setNetworkVariable(0, "ssid", sHexSSID); 504 505 List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel); 506 assertEquals(1, configs.size()); 507 508 WifiConfiguration config2 = configs.get(0); 509 assertEquals("\"GoogleGuest\"", config2.SSID); 510 assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)); 511 } 512 513 @Test 514 public void scan() throws Exception { 515 516 addNetwork(); 517 518 mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); 519 520 mWsm.startScan(-1, 0, null, null); 521 wait(200); 522 523 ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor = 524 ArgumentCaptor.forClass(WifiScanner.ScanSettings.class); 525 ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor = 526 ArgumentCaptor.forClass(WifiScanner.ScanListener.class); 527 verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture()); 528 assertEquals("band", WifiScanner.WIFI_BAND_BOTH_WITH_DFS, 529 scanSettingsCaptor.getValue().band); 530 assertEquals("reportEvents", WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN 531 | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, 532 scanSettingsCaptor.getValue().reportEvents); 533 534 ScanResult[] results = getMockScanResults(); 535 for (ScanResult result : results) { 536 scanListenerCaptor.getValue().onFullResult(result); 537 } 538 scanListenerCaptor.getValue().onResults( 539 new WifiScanner.ScanData[] {new WifiScanner.ScanData(0, 0, results)}); 540 541 wait(200); 542 List<ScanResult> reportedResults = mWsm.syncGetScanResultsList(); 543 assertEquals(8, reportedResults.size()); 544 } 545 546 @Test 547 public void connect() throws Exception { 548 549 addNetwork(); 550 551 mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); 552 mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true); 553 wait(200); 554 555 verify(mWifiNative).enableNetwork(0, true); 556 557 mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); 558 mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, 559 new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); 560 wait(200); 561 562 assertEquals("ObtainingIpState", getCurrentState().getName()); 563 564 DhcpResults dhcpResults = new DhcpResults(); 565 dhcpResults.setGateway("1.2.3.4"); 566 dhcpResults.setIpAddress("192.168.1.100", 0); 567 dhcpResults.addDns("8.8.8.8"); 568 dhcpResults.setLeaseDuration(3600); 569 570 mWsm.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION, DhcpStateMachine.DHCP_SUCCESS, 0, 571 dhcpResults); 572 wait(200); 573 574 assertEquals("ConnectedState", getCurrentState().getName()); 575 } 576 577 @Test 578 public void disconnect() throws Exception { 579 connect(); 580 581 mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06"); 582 mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, 583 new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); 584 wait(200); 585 586 assertEquals("DisconnectedState", getCurrentState().getName()); 587 } 588} 589