• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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