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