1 /* 2 * Copyright (C) 2018 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.rtt.cts; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assume.assumeTrue; 24 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageManager; 30 import android.location.LocationManager; 31 import android.net.wifi.ScanResult; 32 import android.net.wifi.WifiManager; 33 import android.net.wifi.cts.TestHelper; 34 import android.net.wifi.cts.WifiFeature; 35 import android.net.wifi.cts.WifiJUnit4TestBase; 36 import android.net.wifi.rtt.RangingResult; 37 import android.net.wifi.rtt.RangingResultCallback; 38 import android.net.wifi.rtt.WifiRttManager; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.HandlerExecutor; 42 import android.os.HandlerThread; 43 import android.support.test.uiautomator.UiDevice; 44 import android.util.Log; 45 46 import androidx.test.platform.app.InstrumentationRegistry; 47 48 import com.android.compatibility.common.util.PollingCheck; 49 import com.android.compatibility.common.util.ShellIdentityUtils; 50 import com.android.wifi.flags.Flags; 51 52 import org.junit.AfterClass; 53 import org.junit.Before; 54 import org.junit.BeforeClass; 55 56 import java.util.ArrayList; 57 import java.util.Collection; 58 import java.util.Comparator; 59 import java.util.HashMap; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.Random; 63 import java.util.concurrent.CountDownLatch; 64 import java.util.concurrent.Executor; 65 import java.util.concurrent.TimeUnit; 66 67 /** 68 * Base class for Wi-Fi RTT CTS test cases. Provides a uniform configuration and event management 69 * facility. 70 */ 71 public class TestBase extends WifiJUnit4TestBase { 72 protected static final String TAG = "WifiRttCtsTests"; 73 74 // wait for Wi-Fi RTT to become available 75 private static final int WAIT_FOR_RTT_CHANGE_SECS = 10; 76 77 // wait for Wi-Fi scan results to become available 78 private static final int WAIT_FOR_SCAN_RESULTS_SECS = 20; 79 80 // wait for network selection and connection finish 81 private static final int WAIT_FOR_CONNECTION_FINISH_MS = 30_000; 82 83 // Interval between failure scans 84 private static final int INTERVAL_BETWEEN_FAILURE_SCAN_MILLIS = 5_000; 85 86 private static final int DURATION_MILLIS = 10_000; 87 88 // Number of scans to do while searching for APs supporting IEEE 802.11mc 89 private static final int NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP = 5; 90 91 // 5GHz Frequency band 92 private static final int FREQUENCY_OF_5GHZ_BAND_IN_MHZ = 5_000; 93 protected static Context sContext; 94 private static boolean sShouldRunTest; 95 private static UiDevice sUiDevice; 96 private static TestHelper sTestHelper; 97 private static boolean sWasVerboseLoggingEnabled; 98 private static WifiManager sWifiManager; 99 private static Boolean sWasScanThrottleEnabled; 100 private static boolean sWasWifiEnabled; 101 private static ScanResult s11McScanResult; 102 private static ScanResult s11AzScanResult; 103 private static ScanResult s11AzSecureScanResult; 104 private static ScanResult sLegacyScanResult; 105 106 protected WifiRttManager mWifiRttManager; 107 protected Bundle mCharacteristics; 108 109 private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest"); 110 protected final Executor mExecutor; 111 protected final Handler mHandler; 112 113 { mHandlerThread.start()114 mHandlerThread.start(); 115 mHandler = new Handler(mHandlerThread.getLooper()); 116 mExecutor = new HandlerExecutor(mHandler); 117 } 118 119 @BeforeClass setupClass()120 public static void setupClass() throws Exception { 121 sContext = InstrumentationRegistry.getInstrumentation().getContext(); 122 // skip the test if WiFi is not supported 123 // Don't use assumeTrue in @BeforeClass 124 if (!WifiFeature.isWifiSupported(sContext)) { 125 Log.w(TAG, "Wifi not supported. Test wouldn't run"); 126 return; 127 } 128 if (!WifiFeature.isRttSupported(sContext)) { 129 Log.w(TAG, "Wifi RTT not supported. Test wouldn't run"); 130 return; 131 } 132 // skip the test if location is not supported 133 if (!sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION)) { 134 Log.w(TAG, "Location not supported. Test wouldn't run"); 135 return; 136 } 137 // skip if the location is disabled 138 if (!sContext.getSystemService(LocationManager.class).isLocationEnabled()) { 139 Log.w(TAG, "Location is turned off. Test wouldn't run"); 140 return; 141 } 142 143 144 sWifiManager = sContext.getSystemService(WifiManager.class); 145 assertThat(sWifiManager).isNotNull(); 146 sShouldRunTest = true; 147 sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 148 sTestHelper = new TestHelper(sContext, sUiDevice); 149 150 // turn on verbose logging for tests 151 sWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( 152 () -> sWifiManager.isVerboseLoggingEnabled()); 153 ShellIdentityUtils.invokeWithShellPermissions( 154 () -> sWifiManager.setVerboseLoggingEnabled(true)); 155 // Disable scan throttling for tests. 156 sWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions( 157 () -> sWifiManager.isScanThrottleEnabled()); 158 ShellIdentityUtils.invokeWithShellPermissions( 159 () -> sWifiManager.setScanThrottleEnabled(false)); 160 // Disable auto join 161 ShellIdentityUtils.invokeWithShellPermissions( 162 () -> sWifiManager.allowAutojoinGlobal(false)); 163 164 // turn screen on 165 sTestHelper.turnScreenOn(); 166 // enable Wifi 167 sWasWifiEnabled = ShellIdentityUtils.invokeWithShellPermissions( 168 () -> sWifiManager.isWifiEnabled()); 169 if (!sWifiManager.isWifiEnabled()) { 170 ShellIdentityUtils.invokeWithShellPermissions(() -> sWifiManager.setWifiEnabled(true)); 171 } 172 PollingCheck.check("Wifi not enabled", DURATION_MILLIS, () -> sWifiManager.isWifiEnabled()); 173 scanForTestAp(); 174 Thread.sleep(DURATION_MILLIS); 175 } 176 177 @AfterClass tearDownClass()178 public static void tearDownClass() throws Exception { 179 if (!sShouldRunTest) return; 180 181 // turn screen off 182 sTestHelper.turnScreenOff(); 183 ShellIdentityUtils.invokeWithShellPermissions( 184 () -> sWifiManager.setScanThrottleEnabled(sWasScanThrottleEnabled)); 185 ShellIdentityUtils.invokeWithShellPermissions( 186 () -> sWifiManager.setVerboseLoggingEnabled(sWasVerboseLoggingEnabled)); 187 ShellIdentityUtils.invokeWithShellPermissions( 188 () -> sWifiManager.setWifiEnabled(sWasWifiEnabled)); 189 ShellIdentityUtils.invokeWithShellPermissions( 190 () -> sWifiManager.allowAutojoinGlobal(true)); 191 } 192 193 @Before setUp()194 public void setUp() throws Exception { 195 assumeTrue(sShouldRunTest); 196 mWifiRttManager = sContext.getSystemService(WifiRttManager.class); 197 assertNotNull("Wi-Fi RTT Manager", mWifiRttManager); 198 if (!mWifiRttManager.isAvailable()) { 199 IntentFilter intentFilter = new IntentFilter(); 200 intentFilter.addAction(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED); 201 WifiRttBroadcastReceiver receiver = new WifiRttBroadcastReceiver(); 202 sContext.registerReceiver(receiver, intentFilter); 203 assertTrue("Timeout waiting for Wi-Fi RTT to change status", 204 receiver.waitForStateChange()); 205 assertTrue("Wi-Fi RTT is not available (should be)", mWifiRttManager.isAvailable()); 206 } 207 mCharacteristics = mWifiRttManager.getRttCharacteristics(); 208 } 209 210 static class WifiRttBroadcastReceiver extends BroadcastReceiver { 211 private final CountDownLatch mBlocker = new CountDownLatch(1); 212 213 @Override onReceive(Context context, Intent intent)214 public void onReceive(Context context, Intent intent) { 215 if (WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED.equals(intent.getAction())) { 216 mBlocker.countDown(); 217 } 218 } 219 waitForStateChange()220 boolean waitForStateChange() throws InterruptedException { 221 return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS); 222 } 223 } 224 225 static class WifiScansBroadcastReceiver extends BroadcastReceiver { 226 private final CountDownLatch mBlocker = new CountDownLatch(1); 227 228 @Override onReceive(Context context, Intent intent)229 public void onReceive(Context context, Intent intent) { 230 if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { 231 mBlocker.countDown(); 232 } 233 } 234 waitForStateChange()235 boolean waitForStateChange() throws InterruptedException { 236 return mBlocker.await(WAIT_FOR_SCAN_RESULTS_SECS, TimeUnit.SECONDS); 237 } 238 } 239 240 static class ResultCallback extends RangingResultCallback { 241 private final CountDownLatch mBlocker = new CountDownLatch(1); 242 private int mCode; // 0: success, otherwise RangingResultCallback STATUS_CODE_*. 243 private List<RangingResult> mResults; 244 245 @Override onRangingFailure(int code)246 public void onRangingFailure(int code) { 247 mCode = code; 248 mResults = null; // not necessary since intialized to null - but for completeness 249 mBlocker.countDown(); 250 } 251 252 @Override onRangingResults(List<RangingResult> results)253 public void onRangingResults(List<RangingResult> results) { 254 mCode = 0; // not necessary since initialized to 0 - but for completeness 255 mResults = results; 256 mBlocker.countDown(); 257 } 258 259 /** 260 * Waits for the listener callback to be called - or an error (timeout, interruption). 261 * Returns true on callback called, false on error (timeout, interruption). 262 */ waitForCallback()263 boolean waitForCallback() throws InterruptedException { 264 return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS); 265 } 266 267 /** 268 * Returns the code of the callback operation. Will be 0 for success (onRangingResults 269 * called), else (if onRangingFailure called) will be one of the STATUS_CODE_* values. 270 */ getCode()271 int getCode() { 272 return mCode; 273 } 274 275 /** 276 * Returns the list of ranging results. In cases of error (getCode() != 0) will return null. 277 */ getResults()278 List<RangingResult> getResults() { 279 return mResults; 280 } 281 } 282 283 /** 284 * Start a scan and return a list of observed ScanResults (APs). 285 */ scanAps()286 private static List<ScanResult> scanAps() throws InterruptedException { 287 IntentFilter intentFilter = new IntentFilter(); 288 intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 289 WifiScansBroadcastReceiver receiver = new WifiScansBroadcastReceiver(); 290 sContext.registerReceiver(receiver, intentFilter); 291 292 sWifiManager.startScan(); 293 receiver.waitForStateChange(); 294 sContext.unregisterReceiver(receiver); 295 return sWifiManager.getScanResults(); 296 } 297 scanForTestAp()298 private static void scanForTestAp() 299 throws InterruptedException { 300 int scanCount = 0; 301 302 Map<String, ScanResult> ap24Ghz11Mc = new HashMap<>(); 303 Map<String, ScanResult> ap5Ghz11Mc = new HashMap<>(); 304 Map<String, ScanResult> ap24Ghz11Az = new HashMap<>(); 305 Map<String, ScanResult> ap5Ghz11Az = new HashMap<>(); 306 307 while (scanCount <= NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP) { 308 for (ScanResult scanResult : scanAps()) { 309 if (!scanResult.is80211mcResponder() && !scanResult.is80211azNtbResponder()) { 310 if (scanResult.centerFreq0 < FREQUENCY_OF_5GHZ_BAND_IN_MHZ) { 311 continue; 312 } 313 if (sLegacyScanResult == null 314 || scanResult.level > sLegacyScanResult.level) { 315 sLegacyScanResult = scanResult; 316 } 317 continue; 318 } 319 if (scanResult.level < -70) { 320 continue; 321 } 322 if (is24Ghz(scanResult.frequency)) { 323 if (scanResult.is80211azNtbResponder()) { 324 ap24Ghz11Az.put(scanResult.BSSID, scanResult); 325 } 326 if (scanResult.is80211mcResponder()) { 327 ap24Ghz11Mc.put(scanResult.BSSID, scanResult); 328 } 329 } else if (is5Ghz(scanResult.frequency)) { 330 if (scanResult.is80211azNtbResponder()) { 331 ap5Ghz11Az.put(scanResult.BSSID, scanResult); 332 } 333 if (scanResult.is80211mcResponder()) { 334 ap5Ghz11Mc.put(scanResult.BSSID, scanResult); 335 } 336 } 337 } 338 if (sLegacyScanResult == null) { 339 // Ongoing connection may cause scan failure, wait for a while before next scan. 340 Thread.sleep(INTERVAL_BETWEEN_FAILURE_SCAN_MILLIS); 341 } 342 scanCount++; 343 } 344 345 if (!ap5Ghz11Mc.isEmpty()) { 346 s11McScanResult = getRandomScanResult(ap5Ghz11Mc.values()); 347 } else { 348 s11McScanResult = getRandomScanResult(ap24Ghz11Mc.values()); 349 } 350 351 if (!ap5Ghz11Az.isEmpty()) { 352 s11AzScanResult = getRandomScanResult(ap5Ghz11Az.values()); 353 s11AzSecureScanResult = getRandomSecure11azResult(ap5Ghz11Az.values()); 354 } else { 355 s11AzScanResult = getRandomScanResult(ap24Ghz11Az.values()); 356 s11AzSecureScanResult = getRandomSecure11azResult(ap5Ghz11Az.values()); 357 } 358 } 359 getRandomSecure11azResult(Collection<ScanResult> scanResults)360 private static ScanResult getRandomSecure11azResult(Collection<ScanResult> scanResults) { 361 List<ScanResult> secureScanResults = new ArrayList<>(); 362 for (ScanResult scanResult : scanResults) { 363 if (isSecureRangingResponder(scanResult)) { 364 secureScanResults.add(scanResult); 365 } 366 } 367 if (secureScanResults.isEmpty()) return null; 368 int index = new Random().nextInt(secureScanResults.size()); 369 return new ArrayList<>(secureScanResults).get(index); 370 } 371 isSecureRangingResponder(ScanResult scanResult)372 private static boolean isSecureRangingResponder(ScanResult scanResult) { 373 if (!Flags.secureRanging()) return false; 374 return (scanResult.capabilities != null && scanResult.capabilities.contains("PASN") 375 && scanResult.isSecureHeLtfSupported()); 376 } 377 getContext()378 static Context getContext() { 379 return sContext; 380 } 381 getS11AzScanResult()382 static ScanResult getS11AzScanResult() { 383 return s11AzScanResult; 384 } 385 getS11AzSecureScanResult()386 static ScanResult getS11AzSecureScanResult() { 387 return s11AzSecureScanResult; 388 } 389 getS11McScanResult()390 static ScanResult getS11McScanResult() { 391 return s11McScanResult; 392 } 393 getLegacyScanResult()394 static ScanResult getLegacyScanResult() { 395 return sLegacyScanResult; 396 } 397 is24Ghz(int freq)398 private static boolean is24Ghz(int freq) { 399 return freq >= 2142 && freq <= 2484; 400 } 401 is5Ghz(int freq)402 private static boolean is5Ghz(int freq) { 403 return freq >= 5160 && freq <= 5885; 404 } 405 getRandomScanResult(Collection<ScanResult> scanResults)406 private static ScanResult getRandomScanResult(Collection<ScanResult> scanResults) { 407 if (scanResults.isEmpty()) { 408 return null; 409 } 410 int index = new Random().nextInt(scanResults.size()); 411 return new ArrayList<>(scanResults).get(index); 412 } getHighestRssiScanResult(Collection<ScanResult> scanResults)413 private static ScanResult getHighestRssiScanResult(Collection<ScanResult> scanResults) { 414 if (scanResults.isEmpty()) { 415 return null; 416 } 417 return scanResults.stream().max(Comparator.comparingInt(a -> a.level)).get(); 418 } 419 } 420