1 /* 2 * Copyright (C) 2008 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 17 package android.net.wifi.cts; 18 19 import static android.content.Context.RECEIVER_EXPORTED; 20 21 import static com.google.common.truth.Truth.assertThat; 22 import static com.google.common.truth.Truth.assertWithMessage; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assume.assumeTrue; 29 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.net.wifi.MloLink; 35 import android.net.wifi.ScanResult; 36 import android.net.wifi.ScanResult.InformationElement; 37 import android.net.wifi.WifiInfo; 38 import android.net.wifi.WifiManager; 39 import android.net.wifi.WifiManager.WifiLock; 40 import android.net.wifi.WifiScanner; 41 import android.net.wifi.WifiSsid; 42 import android.os.Build; 43 import android.os.Parcel; 44 import android.platform.test.annotations.AppModeFull; 45 import android.platform.test.annotations.RequiresDevice; 46 import android.support.test.uiautomator.UiDevice; 47 48 import androidx.test.ext.junit.runners.AndroidJUnit4; 49 import androidx.test.platform.app.InstrumentationRegistry; 50 51 import com.android.compatibility.common.util.ApiLevelUtil; 52 import com.android.compatibility.common.util.PollingCheck; 53 import com.android.compatibility.common.util.ShellIdentityUtils; 54 55 import org.junit.AfterClass; 56 import org.junit.Before; 57 import org.junit.BeforeClass; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 61 import java.nio.ByteBuffer; 62 import java.nio.charset.StandardCharsets; 63 import java.util.List; 64 65 66 @RunWith(AndroidJUnit4.class) 67 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 68 public class ScanResultTest extends WifiJUnit4TestBase { 69 private static Context sContext; 70 71 private static boolean sShouldRunTest = false; 72 73 private static class MySync { 74 int expectedState = STATE_NULL; 75 } 76 77 private static WifiManager sWifiManager; 78 private static WifiLock sWifiLock; 79 private static TestHelper sTestHelper; 80 private static MySync sMySync; 81 private static boolean sWasVerboseLoggingEnabled; 82 private static boolean sWasScanThrottleEnabled; 83 84 private static final int STATE_NULL = 0; 85 private static final int STATE_WIFI_CHANGING = 1; 86 private static final int STATE_WIFI_CHANGED = 2; 87 private static final int STATE_START_SCAN = 3; 88 private static final int STATE_SCAN_RESULTS_AVAILABLE = 4; 89 private static final int STATE_SCAN_FAILURE = 5; 90 91 private static final String TAG = "WifiInfoTest"; 92 private static final int TIMEOUT_MSEC = 6000; 93 private static final int WAIT_MSEC = 60; 94 private static final int ENABLE_WAIT_MSEC = 10000; 95 private static final int SCAN_WAIT_MSEC = 10000; 96 private static final int SCAN_MAX_RETRY_COUNT = 6; 97 private static final int SCAN_FIND_BSSID_MAX_RETRY_COUNT = 5; 98 private static final long SCAN_FIND_BSSID_WAIT_MSEC = 5_000L; 99 private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000; 100 101 // Note: defined in ScanRequestProxy.java 102 public static final int SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS = 4; 103 104 private static final String TEST_SSID = "TEST_SSID"; 105 public static final String TEST_BSSID = "04:ac:fe:45:34:10"; 106 public static final String TEST_CAPS = "CCMP"; 107 public static final int TEST_LEVEL = -56; 108 public static final int TEST_FREQUENCY = 2412; 109 public static final long TEST_TIMESTAMP = 4660L; 110 111 private static final BroadcastReceiver RECEIVER = new BroadcastReceiver() { 112 @Override 113 public void onReceive(Context context, Intent intent) { 114 final String action = intent.getAction(); 115 if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) { 116 synchronized (sMySync) { 117 sMySync.expectedState = STATE_WIFI_CHANGED; 118 sMySync.notify(); 119 } 120 } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 121 synchronized (sMySync) { 122 if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) { 123 sMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE; 124 } else { 125 sMySync.expectedState = STATE_SCAN_FAILURE; 126 } 127 sMySync.notify(); 128 } 129 } 130 } 131 }; 132 133 @BeforeClass setUpClass()134 public static void setUpClass() throws Exception { 135 sContext = InstrumentationRegistry.getInstrumentation().getContext(); 136 if (!WifiFeature.isWifiSupported(sContext)) { 137 // skip the test if WiFi is not supported 138 return; 139 } 140 sShouldRunTest = true; 141 sMySync = new MySync(); 142 IntentFilter intentFilter = new IntentFilter(); 143 intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 144 intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 145 intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 146 intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 147 intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 148 intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); 149 intentFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); 150 intentFilter.addAction(WifiManager.ACTION_PICK_WIFI_NETWORK); 151 152 if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) { 153 sContext.registerReceiver(RECEIVER, intentFilter, RECEIVER_EXPORTED); 154 } else { 155 sContext.registerReceiver(RECEIVER, intentFilter); 156 } 157 sWifiManager = sContext.getSystemService(WifiManager.class); 158 assertThat(sWifiManager).isNotNull(); 159 160 sTestHelper = new TestHelper(InstrumentationRegistry.getInstrumentation().getContext(), 161 UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())); 162 163 // turn on verbose logging for tests 164 sWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( 165 () -> sWifiManager.isVerboseLoggingEnabled()); 166 ShellIdentityUtils.invokeWithShellPermissions( 167 () -> sWifiManager.setVerboseLoggingEnabled(true)); 168 // Disable scan throttling for tests. 169 sWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions( 170 () -> sWifiManager.isScanThrottleEnabled()); 171 ShellIdentityUtils.invokeWithShellPermissions( 172 () -> sWifiManager.setScanThrottleEnabled(false)); 173 174 sWifiLock = sWifiManager.createWifiLock(TAG); 175 sWifiLock.acquire(); 176 177 // enable Wifi 178 if (!sWifiManager.isWifiEnabled()) setWifiEnabled(true); 179 PollingCheck.check("Wifi not enabled", ENABLE_WAIT_MSEC, 180 () -> sWifiManager.isWifiEnabled()); 181 182 sMySync.expectedState = STATE_NULL; 183 } 184 185 @AfterClass tearDownClass()186 public static void tearDownClass() throws Exception { 187 if (!sShouldRunTest) { 188 return; 189 } 190 sWifiLock.release(); 191 sContext.unregisterReceiver(RECEIVER); 192 if (!sWifiManager.isWifiEnabled()) setWifiEnabled(true); 193 ShellIdentityUtils.invokeWithShellPermissions( 194 () -> sWifiManager.setScanThrottleEnabled(sWasScanThrottleEnabled)); 195 ShellIdentityUtils.invokeWithShellPermissions( 196 () -> sWifiManager.setVerboseLoggingEnabled(sWasVerboseLoggingEnabled)); 197 } 198 199 @Before setUp()200 public void setUp() throws Exception { 201 assumeTrue(sShouldRunTest); 202 } 203 setWifiEnabled(boolean enable)204 private static void setWifiEnabled(boolean enable) throws Exception { 205 synchronized (sMySync) { 206 sMySync.expectedState = STATE_WIFI_CHANGING; 207 ShellIdentityUtils.invokeWithShellPermissions( 208 () -> sWifiManager.setWifiEnabled(enable)); 209 waitForBroadcast(TIMEOUT_MSEC, STATE_WIFI_CHANGED); 210 } 211 } 212 waitForBroadcast(long timeout, int expectedState)213 private static boolean waitForBroadcast(long timeout, int expectedState) throws Exception { 214 long waitTime = System.currentTimeMillis() + timeout; 215 while (System.currentTimeMillis() < waitTime 216 && sMySync.expectedState != expectedState) 217 sMySync.wait(WAIT_MSEC); 218 return sMySync.expectedState == expectedState; 219 } 220 221 @Test testScanResultProperties()222 public void testScanResultProperties() { 223 // this test case should in Wifi environment 224 for (ScanResult scanResult : sWifiManager.getScanResults()) { 225 assertThat(scanResult.toString()).isNotNull(); 226 227 for (InformationElement ie : scanResult.getInformationElements()) { 228 testInformationElementCopyConstructor(ie); 229 testInformationElementFields(ie); 230 } 231 232 assertThat(scanResult.getWifiStandard()).isAnyOf( 233 ScanResult.WIFI_STANDARD_UNKNOWN, 234 ScanResult.WIFI_STANDARD_LEGACY, 235 ScanResult.WIFI_STANDARD_11N, 236 ScanResult.WIFI_STANDARD_11AC, 237 ScanResult.WIFI_STANDARD_11AX, 238 ScanResult.WIFI_STANDARD_11AD, 239 ScanResult.WIFI_STANDARD_11BE 240 ); 241 242 scanResult.isPasspointNetwork(); 243 } 244 } 245 testInformationElementCopyConstructor(InformationElement ie)246 private void testInformationElementCopyConstructor(InformationElement ie) { 247 InformationElement copy = new InformationElement(ie); 248 249 assertThat(copy.getId()).isEqualTo(ie.getId()); 250 assertThat(copy.getIdExt()).isEqualTo(ie.getIdExt()); 251 assertThat(copy.getBytes()).isEqualTo(ie.getBytes()); 252 } 253 testInformationElementFields(InformationElement ie)254 private void testInformationElementFields(InformationElement ie) { 255 // id is 1 octet 256 int id = ie.getId(); 257 assertThat(id).isAtLeast(0); 258 assertThat(id).isAtMost(255); 259 260 // idExt is 0 or 1 octet 261 int idExt = ie.getIdExt(); 262 assertThat(idExt).isAtLeast(0); 263 assertThat(idExt).isAtMost(255); 264 265 ByteBuffer bytes = ie.getBytes(); 266 assertThat(bytes).isNotNull(); 267 } 268 269 /* Multiple scans to ensure bssid is updated */ scanAndWait()270 private void scanAndWait() throws Exception { 271 synchronized (sMySync) { 272 for (int retry = 0; retry < SCAN_MAX_RETRY_COUNT; retry++) { 273 sMySync.expectedState = STATE_START_SCAN; 274 sWifiManager.startScan(); 275 if (waitForBroadcast(SCAN_WAIT_MSEC, STATE_SCAN_RESULTS_AVAILABLE)) { 276 break; 277 } 278 } 279 } 280 } 281 282 @RequiresDevice 283 @Test testScanResultTimeStamp()284 public void testScanResultTimeStamp() throws Exception { 285 long timestamp = 0; 286 String BSSID = null; 287 288 scanAndWait(); 289 290 List<ScanResult> scanResults = sWifiManager.getScanResults(); 291 for (ScanResult result : scanResults) { 292 BSSID = result.BSSID; 293 timestamp = result.timestamp; 294 assertThat(timestamp).isNotEqualTo(0); 295 break; 296 } 297 298 scanAndWait(); 299 300 scanResults = sWifiManager.getScanResults(); 301 for (ScanResult result : scanResults) { 302 if (result.BSSID.equals(BSSID)) { 303 long timeDiff = (result.timestamp - timestamp) / 1000; 304 assertThat(timeDiff).isGreaterThan(0L); 305 assertThat(timeDiff).isLessThan(6L * SCAN_WAIT_MSEC); 306 } 307 } 308 } 309 310 /** Test that the copy constructor copies fields correctly. */ 311 @Test testScanResultConstructors()312 public void testScanResultConstructors() { 313 ScanResult scanResult = new ScanResult(); 314 scanResult.SSID = TEST_SSID; 315 scanResult.setWifiSsid(WifiSsid.fromBytes(TEST_SSID.getBytes(StandardCharsets.UTF_8))); 316 scanResult.BSSID = TEST_BSSID; 317 scanResult.capabilities = TEST_CAPS; 318 scanResult.level = TEST_LEVEL; 319 scanResult.frequency = TEST_FREQUENCY; 320 scanResult.timestamp = TEST_TIMESTAMP; 321 322 ScanResult scanResult2 = new ScanResult(scanResult); 323 assertThat(scanResult2.SSID).isEqualTo(TEST_SSID); 324 assertThat(scanResult2.getWifiSsid()).isEqualTo(scanResult.getWifiSsid()); 325 assertThat(scanResult2.BSSID).isEqualTo(TEST_BSSID); 326 assertThat(scanResult2.capabilities).isEqualTo(TEST_CAPS); 327 assertThat(scanResult2.level).isEqualTo(TEST_LEVEL); 328 assertThat(scanResult2.frequency).isEqualTo(TEST_FREQUENCY); 329 assertThat(scanResult2.timestamp).isEqualTo(TEST_TIMESTAMP); 330 } 331 332 @Test testScanResultMatchesWifiInfo()333 public void testScanResultMatchesWifiInfo() throws Exception { 334 // ensure Wifi is connected 335 ShellIdentityUtils.invokeWithShellPermissions(() -> sWifiManager.reconnect()); 336 PollingCheck.check( 337 "Wifi not connected", 338 WIFI_CONNECT_TIMEOUT_MILLIS, 339 () -> sWifiManager.getConnectionInfo().getNetworkId() != -1); 340 341 final WifiInfo wifiInfo = sWifiManager.getConnectionInfo(); 342 assertThat(wifiInfo).isNotNull(); 343 344 ScanResult currentNetwork = null; 345 for (int i = 0; i < SCAN_FIND_BSSID_MAX_RETRY_COUNT; i++) { 346 scanAndWait(); 347 final List<ScanResult> scanResults = sWifiManager.getScanResults(); 348 currentNetwork = scanResults.stream().filter(r -> r.BSSID.equals(wifiInfo.getBSSID())) 349 .findAny().orElse(null); 350 351 if (currentNetwork != null) { 352 break; 353 } 354 Thread.sleep(SCAN_FIND_BSSID_WAIT_MSEC); 355 } 356 assertWithMessage("Current network not found in scan results") 357 .that(currentNetwork).isNotNull(); 358 359 String wifiInfoSsidQuoted = wifiInfo.getSSID(); 360 String scanResultSsidUnquoted = currentNetwork.SSID; 361 362 assertWithMessage( 363 "SSID mismatch: make sure this isn't a hidden network or an SSID containing " 364 + "non-UTF-8 characters - neither is supported by this CTS test.") 365 .that("\"" + scanResultSsidUnquoted + "\"") 366 .isEqualTo(wifiInfoSsidQuoted); 367 assertThat(currentNetwork.frequency).isEqualTo(wifiInfo.getFrequency()); 368 assertThat(currentNetwork.getSecurityTypes()) 369 .asList().contains(wifiInfo.getCurrentSecurityType()); 370 } 371 372 /** 373 * Verify that scan throttling is enforced. 374 */ 375 @Test testScanThrottling()376 public void testScanThrottling() throws Exception { 377 // re-enable scan throttling 378 ShellIdentityUtils.invokeWithShellPermissions( 379 () -> sWifiManager.setScanThrottleEnabled(true)); 380 sTestHelper.turnScreenOn(); 381 382 try { 383 synchronized (sMySync) { 384 for (int i = 0; i < SCAN_REQUEST_THROTTLE_MAX_IN_TIME_WINDOW_FG_APPS; ++i) { 385 sWifiManager.startScan(); 386 // TODO(b/277663385): Increased timeout until cuttlefish's mac80211_hwsim uses 387 // more than 1 channel. 388 assertTrue("Iteration #" + i, 389 waitForBroadcast(SCAN_WAIT_MSEC * 2, STATE_SCAN_RESULTS_AVAILABLE)); 390 } 391 392 sWifiManager.startScan(); 393 assertTrue("Should be throttled", 394 waitForBroadcast(SCAN_WAIT_MSEC, STATE_SCAN_FAILURE)); 395 } 396 } finally { 397 ShellIdentityUtils.invokeWithShellPermissions( 398 () -> sWifiManager.setScanThrottleEnabled(false)); 399 } 400 } 401 402 /** 403 * Test MLO Attributes in ScanResult Constructor (WiFi-7) 404 */ 405 @Test testScanResultMloAttributes()406 public void testScanResultMloAttributes() { 407 ScanResult scanResult = new ScanResult(); 408 assertNull(scanResult.getApMldMacAddress()); 409 assertEquals(MloLink.INVALID_MLO_LINK_ID, scanResult.getApMloLinkId()); 410 assertNotNull(scanResult.getAffiliatedMloLinks()); 411 assertTrue(scanResult.getAffiliatedMloLinks().isEmpty()); 412 } 413 414 /** 415 * Test MLO Link Constructor (WiFi-7) 416 */ 417 @Test testMloLinkConstructor()418 public void testMloLinkConstructor() { 419 MloLink mloLink = new MloLink(); 420 assertEquals(WifiScanner.WIFI_BAND_UNSPECIFIED, mloLink.getBand()); 421 assertEquals(0, mloLink.getChannel()); 422 assertEquals(MloLink.INVALID_MLO_LINK_ID, mloLink.getLinkId()); 423 assertNull(mloLink.getStaMacAddress()); 424 assertNull(mloLink.getApMacAddress()); 425 assertEquals(MloLink.MLO_LINK_STATE_UNASSOCIATED, mloLink.getState()); 426 assertEquals(WifiInfo.INVALID_RSSI, mloLink.getRssi()); 427 assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, mloLink.getRxLinkSpeedMbps()); 428 assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, mloLink.getTxLinkSpeedMbps()); 429 } 430 431 /** 432 * Test MLO Link parcelable APIs 433 */ 434 @Test testMloLinkParcelable()435 public void testMloLinkParcelable() { 436 Parcel p = Parcel.obtain(); 437 MloLink mloLink = new MloLink(); 438 439 mloLink.writeToParcel(p, 0); 440 p.setDataPosition(0); 441 MloLink newMloLink = MloLink.CREATOR.createFromParcel(p); 442 assertEquals(mloLink, newMloLink); 443 assertEquals("hashCode() did not get right hashCode", 444 mloLink.hashCode(), newMloLink.hashCode()); 445 } 446 } 447