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