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