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 package com.android.settingslib.wifi; 17 18 import android.content.Intent; 19 import android.net.NetworkInfo; 20 import android.net.NetworkInfo.State; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiInfo; 24 import android.net.wifi.WifiManager; 25 import android.net.wifi.WifiSsid; 26 import android.os.HandlerThread; 27 import android.os.Looper; 28 import android.util.Log; 29 30 import com.android.settingslib.BaseTest; 31 import com.android.settingslib.wifi.WifiTracker.Scanner; 32 import com.android.settingslib.wifi.WifiTracker.WifiListener; 33 34 import org.mockito.ArgumentCaptor; 35 import org.mockito.Mockito; 36 37 import java.io.PrintWriter; 38 import java.io.StringWriter; 39 import java.util.ArrayList; 40 import java.util.List; 41 42 public class WifiTrackerTest extends BaseTest { 43 44 private static final String TAG = "WifiTrackerTest"; 45 46 private static final String[] TEST_SSIDS = new String[] { 47 "TEST_SSID_1", 48 "TEST_SSID_2", 49 "TEST_SSID_3", 50 "TEST_SSID_4", 51 "TEST_SSID_5", 52 }; 53 private static final int NUM_NETWORKS = 5; 54 55 private WifiManager mWifiManager; 56 private WifiListener mWifiListener; 57 58 private WifiTracker mWifiTracker; 59 60 private HandlerThread mWorkerThread; 61 private Looper mLooper; 62 private HandlerThread mMainThread; 63 private Looper mMainLooper; 64 65 @Override 66 protected void setUp() throws Exception { 67 super.setUp(); 68 69 mWifiManager = Mockito.mock(WifiManager.class); 70 mWifiListener = Mockito.mock(WifiListener.class); 71 mWorkerThread = new HandlerThread("TestHandlerThread"); 72 mWorkerThread.start(); 73 mLooper = mWorkerThread.getLooper(); 74 mMainThread = new HandlerThread("TestHandlerThread"); 75 mMainThread.start(); 76 mMainLooper = mMainThread.getLooper(); 77 mWifiTracker = new WifiTracker(mContext, mWifiListener, mLooper, true, true, true, 78 mWifiManager, mMainLooper); 79 mWifiTracker.mScanner = mWifiTracker.new Scanner(); 80 Mockito.when(mWifiManager.isWifiEnabled()).thenReturn(true); 81 } 82 83 @Override 84 protected void tearDown() throws Exception { 85 StringWriter sw = new StringWriter(); 86 PrintWriter pw = new PrintWriter(sw); 87 mWifiTracker.dump(pw); 88 pw.flush(); 89 Log.d(TAG, sw.toString()); 90 super.tearDown(); 91 } 92 93 public void testAccessPointsCallback() { 94 sendScanResultsAndProcess(false); 95 96 Mockito.verify(mWifiListener, Mockito.atLeastOnce()).onAccessPointsChanged(); 97 } 98 99 public void testConnectedCallback() { 100 sendConnected(); 101 waitForThreads(); 102 103 Mockito.verify(mWifiListener, Mockito.atLeastOnce()).onConnectedChanged(); 104 assertEquals(true, mWifiTracker.isConnected()); 105 } 106 107 public void testWifiStateCallback() { 108 final int TEST_WIFI_STATE = WifiManager.WIFI_STATE_ENABLED; 109 110 Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); 111 i.putExtra(WifiManager.EXTRA_WIFI_STATE, TEST_WIFI_STATE); 112 mWifiTracker.mReceiver.onReceive(mContext, i); 113 waitForThreads(); 114 115 ArgumentCaptor<Integer> wifiState = ArgumentCaptor.forClass(Integer.class); 116 Mockito.verify(mWifiListener, Mockito.atLeastOnce()) 117 .onWifiStateChanged(wifiState.capture()); 118 assertEquals(TEST_WIFI_STATE, (int) wifiState.getValue()); 119 } 120 121 public void testScanner() { 122 // TODO: Figure out how to verify more of the Scanner functionality. 123 // Make scans be successful. 124 Mockito.when(mWifiManager.startScan()).thenReturn(true); 125 126 mWifiTracker.mScanner.handleMessage(mWifiTracker.mScanner.obtainMessage(Scanner.MSG_SCAN)); 127 Mockito.verify(mWifiManager, Mockito.atLeastOnce()).startScan(); 128 } 129 130 public void testNetworkSorting() { 131 List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>(); 132 List<ScanResult> scanResults = new ArrayList<ScanResult>(); 133 String[] expectedSsids = generateTestNetworks(wifiConfigs, scanResults, true); 134 135 // Tell WifiTracker we are connected now. 136 sendConnected(); 137 138 // Send all of the configs and scan results to the tracker. 139 Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs); 140 Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults); 141 sendScanResultsAndProcess(false); 142 143 List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 144 assertEquals("Expected number of results", NUM_NETWORKS, accessPoints.size()); 145 for (int i = 0; i < NUM_NETWORKS; i++) { 146 assertEquals("Verifying slot " + i, expectedSsids[i], accessPoints.get(i).getSsid()); 147 } 148 } 149 150 public void testSavedOnly() { 151 mWifiTracker = new WifiTracker(mContext, mWifiListener, mLooper, true, false, true, 152 mWifiManager, mMainLooper); 153 mWifiTracker.mScanner = mWifiTracker.new Scanner(); 154 155 List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>(); 156 List<ScanResult> scanResults = new ArrayList<ScanResult>(); 157 generateTestNetworks(wifiConfigs, scanResults, true); 158 159 // Tell WifiTracker we are connected now. 160 sendConnected(); 161 162 // Send all of the configs and scan results to the tracker. 163 Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs); 164 Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults); 165 sendScanResultsAndProcess(false); 166 167 List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 168 // Only expect the first two to come back in the results. 169 assertEquals("Expected number of results", 2, accessPoints.size()); 170 assertEquals(TEST_SSIDS[1], accessPoints.get(0).getSsid()); 171 assertEquals(TEST_SSIDS[0], accessPoints.get(1).getSsid()); 172 } 173 174 /** 175 * This tests the case where Settings runs this on a non-looper thread for indexing. 176 */ 177 public void testSavedOnlyNoLooper() { 178 mWifiTracker = new WifiTracker(mContext, mWifiListener, mLooper, true, false, true, 179 mWifiManager, null); 180 mWifiTracker.mScanner = mWifiTracker.new Scanner(); 181 182 List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>(); 183 List<ScanResult> scanResults = new ArrayList<ScanResult>(); 184 generateTestNetworks(wifiConfigs, scanResults, true); 185 186 // Send all of the configs and scan results to the tracker. 187 Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs); 188 Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults); 189 mWifiTracker.forceUpdate(); 190 191 List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 192 // Only expect the first two to come back in the results. 193 assertEquals("Expected number of results", 2, accessPoints.size()); 194 assertEquals(TEST_SSIDS[1], accessPoints.get(0).getSsid()); 195 assertEquals(TEST_SSIDS[0], accessPoints.get(1).getSsid()); 196 } 197 198 public void testAvailableOnly() { 199 mWifiTracker = new WifiTracker(mContext, mWifiListener, mLooper, false, true, true, 200 mWifiManager, mMainLooper); 201 mWifiTracker.mScanner = mWifiTracker.new Scanner(); 202 203 List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>(); 204 List<ScanResult> scanResults = new ArrayList<ScanResult>(); 205 String[] expectedSsids = generateTestNetworks(wifiConfigs, scanResults, true); 206 207 // Tell WifiTracker we are connected now. 208 sendConnected(); 209 210 // Send all of the configs and scan results to the tracker. 211 Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs); 212 Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults); 213 sendScanResultsAndProcess(false); 214 215 // Expect the last one (sorted order) to be left off since its only saved. 216 List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 217 assertEquals("Expected number of results", NUM_NETWORKS - 1, accessPoints.size()); 218 for (int i = 0; i < NUM_NETWORKS - 1; i++) { 219 assertEquals("Verifying slot " + i, expectedSsids[i], accessPoints.get(i).getSsid()); 220 } 221 } 222 223 public void testNonEphemeralConnected() { 224 mWifiTracker = new WifiTracker(mContext, mWifiListener, mLooper, false, true, true, 225 mWifiManager, mMainLooper); 226 mWifiTracker.mScanner = mWifiTracker.new Scanner(); 227 228 List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>(); 229 List<ScanResult> scanResults = new ArrayList<ScanResult>(); 230 generateTestNetworks(wifiConfigs, scanResults, false); 231 232 // Tell WifiTracker we are connected now. 233 sendConnected(); 234 235 // Send all of the configs and scan results to the tracker. 236 Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs); 237 Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults); 238 // Do this twice to catch a bug that was happening in the caching, making things ephemeral. 239 sendScanResultsAndProcess(true); 240 241 List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints(); 242 assertEquals("Expected number of results", NUM_NETWORKS - 1, accessPoints.size()); 243 assertFalse("Connection is not ephemeral", accessPoints.get(0).isEphemeral()); 244 assertTrue("Connected to wifi", accessPoints.get(0).isActive()); 245 } 246 247 public void testEnableResumeScanning() { 248 mWifiTracker.mScanner = null; 249 250 Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); 251 // Make sure disable/enable cycle works with no scanner (no crashing). 252 i.putExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); 253 mWifiTracker.mReceiver.onReceive(mContext, i); 254 i.putExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_ENABLED); 255 mWifiTracker.mReceiver.onReceive(mContext, i); 256 257 Mockito.when(mWifiManager.isWifiEnabled()).thenReturn(false); 258 i.putExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); 259 mWifiTracker.mReceiver.onReceive(mContext, i); 260 // Now enable scanning while wifi is off, it shouldn't start. 261 mWifiTracker.resumeScanning(); 262 assertFalse(mWifiTracker.mScanner.isScanning()); 263 264 // Turn on wifi and make sure scanning starts. 265 i.putExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_ENABLED); 266 mWifiTracker.mReceiver.onReceive(mContext, i); 267 assertTrue(mWifiTracker.mScanner.isScanning()); 268 } 269 270 private String[] generateTestNetworks(List<WifiConfiguration> wifiConfigs, 271 List<ScanResult> scanResults, boolean connectedIsEphemeral) { 272 String[] expectedSsids = new String[NUM_NETWORKS]; 273 274 // First is just saved; 275 addConfig(wifiConfigs, TEST_SSIDS[0]); 276 // This should come last since its not available. 277 expectedSsids[4] = TEST_SSIDS[0]; 278 279 // Second is saved and available. 280 addConfig(wifiConfigs, TEST_SSIDS[1]); 281 addResult(scanResults, TEST_SSIDS[1], 0); 282 // This one is going to have a couple extra results, to verify de-duplication. 283 addResult(scanResults, TEST_SSIDS[1], 2); 284 addResult(scanResults, TEST_SSIDS[1], 1); 285 // This should come second since it is available and saved but not connected. 286 expectedSsids[1] = TEST_SSIDS[1]; 287 288 // Third is just available, but higher rssi. 289 addResult(scanResults, TEST_SSIDS[2], 3); 290 // This comes after the next one since it has a lower rssi. 291 expectedSsids[3] = TEST_SSIDS[2]; 292 293 // Fourth also just available but with even higher rssi. 294 addResult(scanResults, TEST_SSIDS[3], 4); 295 // This is the highest rssi but not saved so it should be after the saved+availables. 296 expectedSsids[2] = TEST_SSIDS[3]; 297 298 // Last is going to be connected. 299 int netId = WifiConfiguration.INVALID_NETWORK_ID; 300 if (!connectedIsEphemeral) { 301 netId = addConfig(wifiConfigs, TEST_SSIDS[4]); 302 } 303 addResult(scanResults, TEST_SSIDS[4], 2); 304 // Setup wifi connection to be this one. 305 WifiInfo wifiInfo = Mockito.mock(WifiInfo.class); 306 Mockito.when(wifiInfo.getSSID()).thenReturn(TEST_SSIDS[4]); 307 Mockito.when(wifiInfo.getNetworkId()).thenReturn(netId); 308 Mockito.when(mWifiManager.getConnectionInfo()).thenReturn(wifiInfo); 309 // This should come first since it is connected. 310 expectedSsids[0] = TEST_SSIDS[4]; 311 312 return expectedSsids; 313 } 314 315 private void addResult(List<ScanResult> results, String ssid, int level) { 316 results.add(new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), 317 ssid, ssid, levelToRssi(level), AccessPoint.LOWER_FREQ_24GHZ, 0)); 318 } 319 320 public static int levelToRssi(int level) { 321 // Reverse level to rssi calculation based off from WifiManager.calculateSignalLevel. 322 final int MAX_RSSI = -55; 323 final int MIN_RSSI = -100; 324 final int NUM_LEVELS = 4; 325 return level * (MAX_RSSI - MIN_RSSI) / (NUM_LEVELS - 1) + MIN_RSSI; 326 } 327 328 private int addConfig(List<WifiConfiguration> configs, String ssid) { 329 WifiConfiguration config = new WifiConfiguration(); 330 config.networkId = configs.size(); 331 config.SSID = '"' + ssid + '"'; 332 configs.add(config); 333 return config.networkId; 334 } 335 336 private void sendConnected() { 337 NetworkInfo networkInfo = Mockito.mock(NetworkInfo.class); 338 Mockito.when(networkInfo.isConnected()).thenReturn(true); 339 Mockito.when(networkInfo.getState()).thenReturn(State.CONNECTED); 340 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 341 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); 342 mWifiTracker.mReceiver.onReceive(mContext, intent); 343 } 344 345 private void sendScanResultsAndProcess(boolean sendTwice) { 346 Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 347 mWifiTracker.mReceiver.onReceive(mContext, i); 348 if (sendTwice) { 349 mWifiTracker.mReceiver.onReceive(mContext, i); 350 } 351 waitForThreads(); 352 } 353 354 private void waitForThreads() { 355 // Run all processing. 356 mWorkerThread.quitSafely(); 357 try { 358 mWorkerThread.join(); 359 } catch (InterruptedException e) { 360 } 361 // Send all callbacks. 362 mMainThread.quitSafely(); 363 try { 364 mMainThread.join(); 365 } catch (InterruptedException e) { 366 } 367 } 368 369 } 370