WifiStateMachineTest.java revision c79666b79e273ceaa2f74090b02ca6cf83c61387
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.MockAnswerUtil.AnswerWithArguments; 67import com.android.server.wifi.hotspot2.NetworkDetail; 68import com.android.server.wifi.hotspot2.SupplicantBridge; 69import com.android.server.wifi.hotspot2.omadm.MOManager; 70import com.android.server.wifi.hotspot2.osu.OSUManager; 71import com.android.server.wifi.p2p.WifiP2pServiceImpl; 72 73import org.junit.After; 74import org.junit.Before; 75import org.junit.Test; 76import org.mockito.Mock; 77import org.mockito.Mockito; 78import org.mockito.MockitoAnnotations; 79 80import java.io.ByteArrayOutputStream; 81import java.io.PrintWriter; 82import java.lang.reflect.Field; 83import java.lang.reflect.InvocationTargetException; 84import java.lang.reflect.Method; 85import java.util.ArrayList; 86import java.util.HashMap; 87import java.util.List; 88import java.util.Map; 89 90/** 91 * Unit tests for {@link com.android.server.wifi.WifiStateMachine}. 92 */ 93@SmallTest 94public class WifiStateMachineTest { 95 public static final String TAG = "WifiStateMachineTest"; 96 97 private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) { 98 return mock(class1, withSettings().extraInterfaces(interfaces)); 99 } 100 101 private static <T, I> IBinder mockService(Class<T> class1, Class<I> iface) { 102 T tImpl = mockWithInterfaces(class1, iface); 103 IBinder binder = mock(IBinder.class); 104 when(((IInterface) tImpl).asBinder()).thenReturn(binder); 105 when(binder.queryLocalInterface(iface.getCanonicalName())) 106 .thenReturn((IInterface) tImpl); 107 return binder; 108 } 109 110 private void enableDebugLogs() { 111 mWsm.enableVerboseLogging(1); 112 } 113 114 private static void installWlanWifiNative(WifiNative wifiNative) throws Exception { 115 Field field = WifiNative.class.getDeclaredField("wlanNativeInterface"); 116 field.setAccessible(true); 117 field.set(null, wifiNative); 118 119 when(wifiNative.getInterfaceName()).thenReturn("wlan0"); 120 } 121 122 private FrameworkFacade getFrameworkFacade() throws InterruptedException { 123 FrameworkFacade facade = mock(FrameworkFacade.class); 124 125 when(facade.makeBaseLogger()).thenReturn(mock(BaseWifiLogger.class)); 126 when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn( 127 mockWithInterfaces(IBinder.class, INetworkManagementService.class)); 128 129 IBinder p2pBinder = mockService(WifiP2pServiceImpl.class, IWifiP2pManager.class); 130 when(facade.getService(Context.WIFI_P2P_SERVICE)).thenReturn(p2pBinder); 131 132 WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface( 133 IWifiP2pManager.class.getCanonicalName()); 134 135 final Object sync = new Object(); 136 synchronized (sync) { 137 mP2pThread = new HandlerThread("WifiP2pMockThread") { 138 @Override 139 protected void onLooperPrepared() { 140 synchronized (sync) { 141 sync.notifyAll(); 142 } 143 } 144 }; 145 146 mP2pThread.start(); 147 sync.wait(); 148 } 149 150 Handler handler = new Handler(mP2pThread.getLooper()); 151 when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler)); 152 153 IBinder batteryStatsBinder = mockService(BatteryStats.class, IBatteryStats.class); 154 when(facade.getService(BatteryStats.SERVICE_NAME)).thenReturn(batteryStatsBinder); 155 156 when(facade.makeOsuManager(any(WifiConfigStore.class), any(Context.class), any( 157 SupplicantBridge.class), any(MOManager.class), any(WifiStateMachine.class))) 158 .thenReturn(mock(OSUManager.class)); 159 160 when(facade.makeDhcpStateMachine( 161 any(Context.class), any(StateMachine.class), any(String.class))).thenReturn( 162 mock(BaseDhcpStateMachine.class)); 163 164 when(facade.makeIpReachabilityMonitor(any(Context.class), anyString(), 165 any(IpReachabilityMonitor.Callback.class))).thenReturn( 166 mock(IpReachabilityMonitor.class)); 167 168 return facade; 169 } 170 171 private Context getContext() throws Exception { 172 PackageManager pkgMgr = mock(PackageManager.class); 173 when(pkgMgr.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true); 174 175 Context context = mock(Context.class); 176 when(context.getPackageManager()).thenReturn(pkgMgr); 177 when(context.getContentResolver()).thenReturn(mock(ContentResolver.class)); 178 179 MockResources resources = new com.android.server.wifi.MockResources(); 180 when(context.getResources()).thenReturn(resources); 181 182 ContentResolver cr = mock(ContentResolver.class); 183 when(context.getContentResolver()).thenReturn(cr); 184 185 when(context.getSystemService(Context.POWER_SERVICE)).thenReturn( 186 new PowerManager(context, mock(IPowerManager.class), new Handler())); 187 188 mAlarmManager = new MockAlarmManager(); 189 when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn( 190 mAlarmManager.getAlarmManager()); 191 192 when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( 193 mock(ConnectivityManager.class)); 194 195 return context; 196 } 197 198 private Resources getMockResources() { 199 MockResources resources = new MockResources(); 200 resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false); 201 return resources; 202 } 203 204 private IState getCurrentState() throws 205 NoSuchMethodException, InvocationTargetException, IllegalAccessException { 206 Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); 207 method.setAccessible(true); 208 return (IState) method.invoke(mWsm); 209 } 210 211 private static HandlerThread getWsmHandlerThread(WifiStateMachine wsm) throws 212 NoSuchFieldException, InvocationTargetException, IllegalAccessException { 213 Field field = StateMachine.class.getDeclaredField("mSmThread"); 214 field.setAccessible(true); 215 return (HandlerThread) field.get(wsm); 216 } 217 218 private static void stopLooper(final Looper looper) throws Exception { 219 new Handler(looper).post(new Runnable() { 220 @Override 221 public void run() { 222 looper.quitSafely(); 223 } 224 }); 225 } 226 227 private void wait(int delayInMs) throws InterruptedException { 228 Looper looper = mWsmThread.getLooper(); 229 final Handler handler = new Handler(looper); 230 synchronized (handler) { 231 handler.postDelayed(new Runnable() { 232 @Override 233 public void run() { 234 synchronized (handler) { 235 handler.notifyAll(); 236 } 237 } 238 }, delayInMs); 239 240 handler.wait(); 241 } 242 } 243 244 private void dumpState() { 245 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 246 PrintWriter writer = new PrintWriter(stream); 247 mWsm.dump(null, writer, null); 248 writer.flush(); 249 Log.d(TAG, "WifiStateMachine state -" + stream.toString()); 250 } 251 252 private static ScanDetail getGoogleGuestScanDetail(int rssi) { 253 ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1]; 254 ie[0] = ScanResults.generateSsidIe(sSSID); 255 NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq); 256 ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq, 257 Long.MAX_VALUE /* needed so that scan results aren't rejected because 258 there older than scan start */); 259 return detail; 260 } 261 262 private ArrayList<ScanDetail> getMockScanResults() { 263 ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825); 264 ArrayList<ScanDetail> list = sr.getScanDetailArrayList(); 265 266 int rssi = -65; 267 list.add(getGoogleGuestScanDetail(rssi)); 268 return list; 269 } 270 271 static final String sSSID = "\"GoogleGuest\""; 272 static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID); 273 static final String sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", ""); 274 static final String sBSSID = "01:02:03:04:05:06"; 275 static final int sFreq = 2437; 276 277 WifiStateMachine mWsm; 278 HandlerThread mWsmThread; 279 HandlerThread mP2pThread; 280 HandlerThread mSyncThread; 281 AsyncChannel mWsmAsyncChannel; 282 MockAlarmManager mAlarmManager; 283 MockWifiMonitor mWifiMonitor; 284 285 @Mock WifiNative mWifiNative; 286 @Mock SupplicantStateTracker mSupplicantStateTracker; 287 @Mock WifiMetrics mWifiMetrics; 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 mWifiMetrics = mock(WifiMetrics.class); 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))) 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, mWifiMetrics); 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())) 443 .then(new AnswerWithArguments<Boolean>() { 444 public boolean answer(int netId, String name, String value) { 445 if (netId != 0) { 446 Log.d(TAG, "Can't set var " + name + " for " + netId); 447 return false; 448 } 449 450 Log.d(TAG, "Setting var " + name + " to " + value + " for " + netId); 451 nameToValue.put(name, value); 452 return true; 453 } 454 }); 455 456 when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject())) 457 .then(new AnswerWithArguments<Boolean>() { 458 public boolean answer(int netId, String name, Map<String, String> values) { 459 if (netId != 0) { 460 Log.d(TAG, "Can't set extra " + name + " for " + netId); 461 return false; 462 } 463 464 Log.d(TAG, "Setting extra for " + netId); 465 return true; 466 } 467 }); 468 469 when(mWifiNative.getNetworkVariable(anyInt(), anyString())) 470 .then(new AnswerWithArguments<String>() { 471 public String answer(int netId, String name) throws Throwable { 472 if (netId != 0) { 473 Log.d(TAG, "Can't find var " + name + " for " + netId); 474 return null; 475 } 476 String value = nameToValue.get(name); 477 if (value != null) { 478 Log.d(TAG, "Returning var " + name + " to " + value + " for " + netId); 479 } else { 480 Log.d(TAG, "Can't find var " + name + " for " + netId); 481 } 482 return value; 483 } 484 }); 485 486 WifiConfiguration config = new WifiConfiguration(); 487 config.SSID = sSSID; 488 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 489 mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config); 490 wait(200); 491 492 verify(mWifiNative).addNetwork(); 493 verify(mWifiNative).setNetworkVariable(0, "ssid", sHexSSID); 494 495 List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel); 496 assertEquals(1, configs.size()); 497 498 WifiConfiguration config2 = configs.get(0); 499 assertEquals("\"GoogleGuest\"", config2.SSID); 500 assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)); 501 } 502 503 @Test 504 public void scan() throws Exception { 505 506 addNetwork(); 507 508 mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); 509 510 mWsm.startScan(-1, 0, null, null); 511 wait(200); 512 513 verify(mWifiNative).scan(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); 514 515 when(mWifiNative.getScanResults()).thenReturn(getMockScanResults()); 516 mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT); 517 518 wait(200); 519 List<ScanResult> results = mWsm.syncGetScanResultsList(); 520 assertEquals(8, results.size()); 521 } 522 523 @Test 524 public void connect() throws Exception { 525 526 addNetwork(); 527 528 mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); 529 mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true); 530 wait(200); 531 532 verify(mWifiNative).enableNetwork(0, true); 533 534 mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); 535 mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, 536 new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); 537 wait(200); 538 539 assertEquals("ObtainingIpState", getCurrentState().getName()); 540 541 DhcpResults dhcpResults = new DhcpResults(); 542 dhcpResults.setGateway("1.2.3.4"); 543 dhcpResults.setIpAddress("192.168.1.100", 0); 544 dhcpResults.addDns("8.8.8.8"); 545 dhcpResults.setLeaseDuration(3600); 546 547 mWsm.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION, DhcpStateMachine.DHCP_SUCCESS, 0, 548 dhcpResults); 549 wait(200); 550 551 assertEquals("ConnectedState", getCurrentState().getName()); 552 } 553 554 @Test 555 public void disconnect() throws Exception { 556 connect(); 557 558 mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06"); 559 mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, 560 new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED)); 561 wait(200); 562 563 assertEquals("DisconnectedState", getCurrentState().getName()); 564 } 565} 566