[RTT] CTS for the public Wi-Fi RTT APIs
Baseline set of tests for the public Wi-Fi RTT API. Note: some of these tests require an IEEE 802.11mc capable AP. Bug: 63446747 Test: tests pass Change-Id: I176d9adbef15ce1c33b4572d5eb7e6cdf672f021
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||
|
||||
242
tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
Normal file
242
tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.wifi.rtt.cts;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.net.wifi.rtt.RangingResult;
|
||||
import android.net.wifi.rtt.RangingResultCallback;
|
||||
import android.net.wifi.rtt.WifiRttManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.HandlerThread;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Base class for Wi-Fi RTT CTS test cases. Provides a uniform configuration and event management
|
||||
* facility.
|
||||
*/
|
||||
public class TestBase extends AndroidTestCase {
|
||||
protected static final String TAG = "WifiRttCtsTests";
|
||||
|
||||
// The SSID of the test AP which supports IEEE 802.11mc
|
||||
// TODO b/74518964: finalize correct method to refer to an AP in the test lab
|
||||
static final String SSID_OF_TEST_AP = "standalone_rtt";
|
||||
|
||||
// wait for Wi-Fi RTT to become available
|
||||
private static final int WAIT_FOR_RTT_CHANGE_SECS = 10;
|
||||
|
||||
// wait for Wi-Fi scan results to become available
|
||||
private static final int WAIT_FOR_SCAN_RESULTS_SECS = 20;
|
||||
|
||||
protected WifiRttManager mWifiRttManager;
|
||||
protected WifiManager mWifiManager;
|
||||
private LocationManager mLocationManager;
|
||||
private WifiManager.WifiLock mWifiLock;
|
||||
|
||||
private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
|
||||
protected final Executor mExecutor;
|
||||
{
|
||||
mHandlerThread.start();
|
||||
mExecutor = new HandlerExecutor(new Handler(mHandlerThread.getLooper()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag indicating whether or not Wi-Fi RTT should be tested. Wi-Fi RTT
|
||||
* should be tested if the feature is supported on the current device.
|
||||
*/
|
||||
static boolean shouldTestWifiRtt(Context context) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
return pm.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
if (!shouldTestWifiRtt(getContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLocationManager = (LocationManager) getContext().getSystemService(
|
||||
Context.LOCATION_SERVICE);
|
||||
assertTrue("RTT testing requires Location to be enabled",
|
||||
mLocationManager.isLocationEnabled());
|
||||
|
||||
mWifiRttManager = (WifiRttManager) getContext().getSystemService(
|
||||
Context.WIFI_RTT_RANGING_SERVICE);
|
||||
assertNotNull("Wi-Fi RTT Manager", mWifiRttManager);
|
||||
|
||||
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
|
||||
assertNotNull("Wi-Fi Manager", mWifiManager);
|
||||
mWifiLock = mWifiManager.createWifiLock(TAG);
|
||||
mWifiLock.acquire();
|
||||
if (!mWifiManager.isWifiEnabled()) {
|
||||
mWifiManager.setWifiEnabled(true);
|
||||
}
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
|
||||
WifiRttBroadcastReceiver receiver = new WifiRttBroadcastReceiver();
|
||||
mContext.registerReceiver(receiver, intentFilter);
|
||||
if (!mWifiRttManager.isAvailable()) {
|
||||
assertTrue("Timeout waiting for Wi-Fi RTT to change status",
|
||||
receiver.waitForStateChange());
|
||||
assertTrue("Wi-Fi RTT is not available (should be)", mWifiRttManager.isAvailable());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
if (!shouldTestWifiRtt(getContext())) {
|
||||
super.tearDown();
|
||||
return;
|
||||
}
|
||||
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
class WifiRttBroadcastReceiver extends BroadcastReceiver {
|
||||
private CountDownLatch mBlocker = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED.equals(intent.getAction())) {
|
||||
mBlocker.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
boolean waitForStateChange() throws InterruptedException {
|
||||
return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
class WifiScansBroadcastReceiver extends BroadcastReceiver {
|
||||
private CountDownLatch mBlocker = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
|
||||
mBlocker.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
boolean waitForStateChange() throws InterruptedException {
|
||||
return mBlocker.await(WAIT_FOR_SCAN_RESULTS_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultCallback extends RangingResultCallback {
|
||||
private CountDownLatch mBlocker = new CountDownLatch(1);
|
||||
private int mCode; // 0: success, otherwise RangingResultCallback STATUS_CODE_*.
|
||||
private List<RangingResult> mResults;
|
||||
|
||||
@Override
|
||||
public void onRangingFailure(int code) {
|
||||
mCode = code;
|
||||
mResults = null; // not necessary since intialized to null - but for completeness
|
||||
mBlocker.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingResults(List<RangingResult> results) {
|
||||
mCode = 0; // not necessary since initialized to 0 - but for completeness
|
||||
mResults = results;
|
||||
mBlocker.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the listener callback to be called - or an error (timeout, interruption).
|
||||
* Returns true on callback called, false on error (timeout, interruption).
|
||||
*/
|
||||
boolean waitForCallback() throws InterruptedException {
|
||||
return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code of the callback operation. Will be 0 for success (onRangingResults
|
||||
* called), else (if onRangingFailure called) will be one of the STATUS_CODE_* values.
|
||||
*/
|
||||
int getCode() {
|
||||
return mCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of ranging results. In cases of error (getCode() != 0) will return null.
|
||||
*/
|
||||
List<RangingResult> getResults() {
|
||||
return mResults;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a scan and return a list of observed ScanResults (APs).
|
||||
*/
|
||||
protected List<ScanResult> scanAps() throws InterruptedException {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
|
||||
WifiScansBroadcastReceiver receiver = new WifiScansBroadcastReceiver();
|
||||
mContext.registerReceiver(receiver, intentFilter);
|
||||
|
||||
mWifiManager.startScan();
|
||||
receiver.waitForStateChange();
|
||||
mContext.unregisterReceiver(receiver);
|
||||
return mWifiManager.getScanResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a scan and return the test AP with the specified SSID and which supports IEEE 802.11mc.
|
||||
* If the AP is not found re-attempts the scan maxScanRetries times (i.e. total number of
|
||||
* scans can be maxScanRetries + 1).
|
||||
*
|
||||
* Returns null if test AP is not found in the specified number of scans.
|
||||
*
|
||||
* @param ssid The SSID of the test AP
|
||||
* @param maxScanRetries Maximum number of scans retries (in addition to first scan).
|
||||
*/
|
||||
protected ScanResult scanForTestAp(String ssid, int maxScanRetries)
|
||||
throws InterruptedException {
|
||||
int scanCount = 0;
|
||||
while (scanCount <= maxScanRetries) {
|
||||
for (ScanResult scanResult : scanAps()) {
|
||||
if (!scanResult.is80211mcResponder()) {
|
||||
continue;
|
||||
}
|
||||
if (!ssid.equals(scanResult.SSID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return scanResult;
|
||||
}
|
||||
|
||||
scanCount++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
202
tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
Normal file
202
tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.wifi.rtt.cts;
|
||||
|
||||
import android.content.IntentFilter;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.rtt.RangingRequest;
|
||||
import android.net.wifi.rtt.RangingResult;
|
||||
import android.net.wifi.rtt.WifiRttManager;
|
||||
|
||||
import com.android.compatibility.common.util.DeviceReportLog;
|
||||
import com.android.compatibility.common.util.ResultType;
|
||||
import com.android.compatibility.common.util.ResultUnit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wi-Fi RTT CTS test: range to all available Access Points which support IEEE 802.11mc.
|
||||
*/
|
||||
public class WifiRttTest extends TestBase {
|
||||
// Max number of scan retries to do while searching for APs supporting IEEE 802.11mc
|
||||
private static final int MAX_NUM_SCAN_RETRIES_SEARCHING_FOR_IEEE80211MC_AP = 2;
|
||||
|
||||
// Number of RTT measurements per AP
|
||||
private static final int NUM_OF_RTT_ITERATIONS = 10;
|
||||
|
||||
// Maximum failure rate of RTT measurements (percentage)
|
||||
private static final int MAX_FAILURE_RATE_PERCENT = 10;
|
||||
|
||||
// Maximum variation from the average measurement (measures consistency)
|
||||
private static final int MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM = 1000;
|
||||
|
||||
// Minimum valid RSSI value
|
||||
private static final int MIN_VALID_RSSI = -100;
|
||||
|
||||
/**
|
||||
* Test Wi-Fi RTT ranging operation:
|
||||
* - Scan for visible APs for the test AP (which is validated to support IEEE 802.11mc)
|
||||
* - Perform N (constant) RTT operations
|
||||
* - Validate:
|
||||
* - Failure ratio < threshold (constant)
|
||||
* - Result margin < threshold (constant)
|
||||
*/
|
||||
public void testRangingToTestAp() throws InterruptedException {
|
||||
if (!shouldTestWifiRtt(getContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan for IEEE 802.11mc supporting APs
|
||||
ScanResult testAp = scanForTestAp(SSID_OF_TEST_AP,
|
||||
MAX_NUM_SCAN_RETRIES_SEARCHING_FOR_IEEE80211MC_AP);
|
||||
assertTrue("Cannot find test AP", testAp != null);
|
||||
|
||||
// Perform RTT operations
|
||||
RangingRequest request = new RangingRequest.Builder().addAccessPoint(testAp).build();
|
||||
List<RangingResult> allResults = new ArrayList<>();
|
||||
int numFailures = 0;
|
||||
int distanceSum = 0;
|
||||
int distanceMin = 0;
|
||||
int distanceMax = 0;
|
||||
int[] statuses = new int[NUM_OF_RTT_ITERATIONS];
|
||||
int[] distanceMms = new int[NUM_OF_RTT_ITERATIONS];
|
||||
int[] distanceStdDevMms = new int[NUM_OF_RTT_ITERATIONS];
|
||||
int[] rssis = new int[NUM_OF_RTT_ITERATIONS];
|
||||
for (int i = 0; i < NUM_OF_RTT_ITERATIONS; ++i) {
|
||||
ResultCallback callback = new ResultCallback();
|
||||
mWifiRttManager.startRanging(request, mExecutor, callback);
|
||||
assertTrue("Wi-Fi RTT results: no callback on iteration " + i,
|
||||
callback.waitForCallback());
|
||||
|
||||
List<RangingResult> currentResults = callback.getResults();
|
||||
assertTrue("Wi-Fi RTT results: null results (onRangingFailure) on iteration " + i,
|
||||
currentResults != null);
|
||||
assertTrue("Wi-Fi RTT results: unexpected # of results (expect 1) on iteration " + i,
|
||||
currentResults.size() == 1);
|
||||
RangingResult result = currentResults.get(0);
|
||||
assertTrue("Wi-Fi RTT results: invalid result (wrong BSSID) entry on iteration " + i,
|
||||
result.getMacAddress().toString().equals(testAp.BSSID));
|
||||
|
||||
allResults.add(result);
|
||||
int status = result.getStatus();
|
||||
statuses[i] = status;
|
||||
if (status == RangingResult.STATUS_SUCCESS) {
|
||||
distanceSum += result.getDistanceMm();
|
||||
if (i == 0) {
|
||||
distanceMin = result.getDistanceMm();
|
||||
distanceMax = result.getDistanceMm();
|
||||
} else {
|
||||
distanceMin = Math.min(distanceMin, result.getDistanceMm());
|
||||
distanceMax = Math.max(distanceMax, result.getDistanceMm());
|
||||
}
|
||||
|
||||
assertTrue("Wi-Fi RTT results: invalid RSSI on iteration " + i,
|
||||
result.getRssi() >= MIN_VALID_RSSI);
|
||||
|
||||
distanceMms[i - numFailures] = result.getDistanceMm();
|
||||
distanceStdDevMms[i - numFailures] = result.getDistanceStdDevMm();
|
||||
rssis[i - numFailures] = result.getRssi();
|
||||
} else {
|
||||
numFailures++;
|
||||
}
|
||||
}
|
||||
|
||||
// Save results to log
|
||||
int numGoodResults = NUM_OF_RTT_ITERATIONS - numFailures;
|
||||
DeviceReportLog reportLog = new DeviceReportLog(TAG, "testRangingToTestAp");
|
||||
reportLog.addValues("status_codes", statuses, ResultType.NEUTRAL, ResultUnit.NONE);
|
||||
reportLog.addValues("distance_mm", Arrays.copyOf(distanceMms, numGoodResults),
|
||||
ResultType.NEUTRAL, ResultUnit.NONE);
|
||||
reportLog.addValues("distance_stddev_mm", Arrays.copyOf(distanceStdDevMms, numGoodResults),
|
||||
ResultType.NEUTRAL, ResultUnit.NONE);
|
||||
reportLog.addValues("rssi_dbm", Arrays.copyOf(rssis, numGoodResults), ResultType.NEUTRAL,
|
||||
ResultUnit.NONE);
|
||||
reportLog.submit();
|
||||
|
||||
// Analyze results
|
||||
assertTrue("Wi-Fi RTT failure rate exceeds threshold",
|
||||
numFailures <= NUM_OF_RTT_ITERATIONS * MAX_FAILURE_RATE_PERCENT / 100);
|
||||
if (numFailures != NUM_OF_RTT_ITERATIONS) {
|
||||
double distanceAvg = distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures);
|
||||
assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold",
|
||||
(distanceMax - distanceAvg) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
|
||||
assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold",
|
||||
(distanceAvg - distanceMin) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that on Wi-Fi RTT availability change we get a broadcast + the API returns
|
||||
* correct status.
|
||||
*/
|
||||
public void testAvailabilityStatusChange() throws Exception {
|
||||
if (!shouldTestWifiRtt(getContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
|
||||
|
||||
// 1. Disable Wi-Fi
|
||||
WifiRttBroadcastReceiver receiver1 = new WifiRttBroadcastReceiver();
|
||||
mContext.registerReceiver(receiver1, intentFilter);
|
||||
mWifiManager.setWifiEnabled(false);
|
||||
|
||||
assertTrue("Timeout waiting for Wi-Fi RTT to change status",
|
||||
receiver1.waitForStateChange());
|
||||
assertFalse("Wi-Fi RTT is available (should not be)", mWifiRttManager.isAvailable());
|
||||
|
||||
// 2. Enable Wi-Fi
|
||||
WifiRttBroadcastReceiver receiver2 = new WifiRttBroadcastReceiver();
|
||||
mContext.registerReceiver(receiver2, intentFilter);
|
||||
mWifiManager.setWifiEnabled(true);
|
||||
|
||||
assertTrue("Timeout waiting for Wi-Fi RTT to change status",
|
||||
receiver2.waitForStateChange());
|
||||
assertTrue("Wi-Fi RTT is not available (should be)", mWifiRttManager.isAvailable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that when a request contains more range operations than allowed (by API) that we
|
||||
* get an exception.
|
||||
*/
|
||||
public void testRequestTooLarge() {
|
||||
if (!shouldTestWifiRtt(getContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
RangingRequest.Builder builder = new RangingRequest.Builder();
|
||||
for (int i = 0; i < RangingRequest.getMaxPeers() + 1; ++i) {
|
||||
ScanResult dummy = new ScanResult();
|
||||
dummy.BSSID = "00:01:02:03:04:05";
|
||||
builder.addAccessPoint(dummy);
|
||||
}
|
||||
|
||||
try {
|
||||
mWifiRttManager.startRanging(builder.build(), mExecutor, new ResultCallback());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
"Did not receive expected IllegalArgumentException when tried to range to too "
|
||||
+ "many peers",
|
||||
false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user