| /* |
| * 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 static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.mockito.Mockito.mock; |
| |
| import android.net.MacAddress; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.aware.PeerHandle; |
| import android.net.wifi.cts.WifiBuildCompat; |
| import android.net.wifi.cts.WifiFeature; |
| import android.net.wifi.rtt.RangingRequest; |
| import android.net.wifi.rtt.RangingResult; |
| import android.net.wifi.rtt.ResponderConfig; |
| import android.net.wifi.rtt.ResponderLocation; |
| import android.platform.test.annotations.AppModeFull; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.LargeTest; |
| |
| import com.android.compatibility.common.util.DeviceReportLog; |
| import com.android.compatibility.common.util.ResultType; |
| import com.android.compatibility.common.util.ResultUnit; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| 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. |
| */ |
| @AppModeFull(reason = "Cannot get WifiManager in instant app mode") |
| @LargeTest |
| @RunWith(AndroidJUnit4.class) |
| public class WifiRttTest extends TestBase { |
| // Number of scans to do while searching for APs supporting IEEE 802.11mc |
| private static final int NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP = 5; |
| |
| // 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 = 20; |
| |
| // Maximum variation from the average measurement (measures consistency) |
| private static final int MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM = 2000; |
| |
| // Maximum failure rate of one-sided RTT measurements (percentage) |
| private static final int MAX_NON11MC_FAILURE_RATE_PERCENT = 40; |
| |
| // Maximum non-8011mc variation from the average measurement (measures consistency) |
| private static final int MAX_NON11MC_VARIATION_FROM_AVERAGE_DISTANCE_MM = 4000; |
| |
| // Minimum valid RSSI value |
| private static final int MIN_VALID_RSSI = -100; |
| |
| // Valid Mac Address |
| private static final MacAddress MAC = MacAddress.fromString("00:01:02:03:04:05"); |
| |
| // Interval between two ranging request. |
| private static final int INTERVAL_MS = 1000; |
| |
| /** |
| * Test Wi-Fi RTT ranging operation using ScanResults in request: |
| * - 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) |
| */ |
| @Test |
| public void testRangingToTest11mcApUsingScanResult() throws InterruptedException { |
| // Scan for IEEE 802.11mc supporting APs |
| ScanResult testAp = getS11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that " |
| + "your test setup includes them!", testAp); |
| // Perform RTT operations |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| builder.addAccessPoint(testAp); |
| |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| >= RangingRequest.getMinRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| <= RangingRequest.getMaxRttBurstSize()); |
| } |
| |
| RangingRequest request = builder.build(); |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| assertEquals(1, request.getRttResponders().size()); |
| } |
| range11mcApRequest(request, testAp); |
| } |
| |
| /** |
| * Test Wi-Fi RTT ranging using ResponderConfig in the single responder RangingRequest API. |
| * - 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) |
| */ |
| @Test |
| public void testRangingToTest11mcApUsingResponderConfig() throws InterruptedException { |
| // Scan for IEEE 802.11mc supporting APs |
| ScanResult testAp = getS11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that " |
| + "your test setup includes them!", testAp); |
| int preamble = ResponderConfig.fromScanResult(testAp).getPreamble(); |
| |
| // Create a ResponderConfig from the builder API. |
| ResponderConfig.Builder responderBuilder = new ResponderConfig.Builder(); |
| ResponderConfig responder = responderBuilder |
| .setMacAddress(MacAddress.fromString(testAp.BSSID)) |
| .set80211mcSupported(testAp.is80211mcResponder()) |
| .setChannelWidth(testAp.channelWidth) |
| .setFrequencyMhz(testAp.frequency) |
| .setCenterFreq0Mhz(testAp.centerFreq0) |
| .setCenterFreq1Mhz(testAp.centerFreq1) |
| .setPreamble(preamble) |
| .build(); |
| |
| // Validate ResponderConfig.Builder set method arguments match getter methods. |
| assertTrue(responder.getMacAddress().toString().equalsIgnoreCase(testAp.BSSID) |
| && responder.is80211mcSupported() == testAp.is80211mcResponder() |
| && responder.getChannelWidth() == testAp.channelWidth |
| && responder.getFrequencyMhz() == testAp.frequency |
| && responder.getCenterFreq0Mhz() == testAp.centerFreq0 |
| && responder.getCenterFreq1Mhz() == testAp.centerFreq1 |
| && responder.getPreamble() == preamble); |
| |
| // Perform RTT operations |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| builder.addResponder(responder); |
| |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| >= RangingRequest.getMinRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| <= RangingRequest.getMaxRttBurstSize()); |
| } |
| |
| RangingRequest request = builder.build(); |
| |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| assertEquals(1, request.getRttResponders().size()); |
| } |
| range11mcApRequest(request, testAp); |
| } |
| |
| /** |
| * Test Wi-Fi RTT ranging using ResponderConfig in the multi-Responder RangingRequest API. |
| * - 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) |
| */ |
| @Test |
| public void testRangingToTest11mcApUsingListResponderConfig() throws InterruptedException { |
| // Scan for IEEE 802.11mc supporting APs |
| ScanResult testAp = getS11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that " |
| + "your test setup includes them!", testAp); |
| ResponderConfig responder = ResponderConfig.fromScanResult(testAp); |
| // Perform RTT operations |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| List<ResponderConfig> responders = new ArrayList<>(); |
| responders.add(responder); |
| builder.addResponders(responders); |
| |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| >= RangingRequest.getMinRttBurstSize()); |
| assertTrue(RangingRequest.getDefaultRttBurstSize() |
| <= RangingRequest.getMaxRttBurstSize()); |
| } |
| |
| RangingRequest request = builder.build(); |
| |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| assertEquals(1, request.getRttResponders().size()); |
| } |
| range11mcApRequest(request, testAp); |
| } |
| |
| /** |
| * Utility method for validating a ranging request. |
| * |
| * @param request the ranging request that is being tested |
| * @param testAp the original test scan result to provide feedback on failure conditions |
| */ |
| private void range11mcApRequest(RangingRequest request, ScanResult testAp) |
| throws InterruptedException { |
| Thread.sleep(5000); |
| 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]; |
| int[] numAttempted = new int[NUM_OF_RTT_ITERATIONS]; |
| int[] numSuccessful = new int[NUM_OF_RTT_ITERATIONS]; |
| long[] timestampsMs = new long[NUM_OF_RTT_ITERATIONS]; |
| byte[] lastLci = null; |
| byte[] lastLcr = null; |
| 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(); |
| assertNotNull("Wi-Fi RTT results: null results (onRangingFailure) on iteration " + i, |
| currentResults); |
| assertEquals("Wi-Fi RTT results: unexpected # of results (expect 1) on iteration " + i, |
| 1, currentResults.size()); |
| RangingResult result = currentResults.get(0); |
| assertEquals("Wi-Fi RTT results: invalid result (wrong BSSID) entry on iteration " + i, |
| result.getMacAddress().toString(), testAp.BSSID); |
| assertNull("Wi-Fi RTT results: invalid result (non-null PeerHandle) entry on iteration " |
| + i, result.getPeerHandle()); |
| |
| allResults.add(result); |
| int status = result.getStatus(); |
| statuses[i] = status; |
| if (status == RangingResult.STATUS_SUCCESS) { |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| assertEquals( |
| "Wi-Fi RTT results: invalid result (wrong rttBurstSize) entry on " |
| + "iteration " |
| + i, |
| result.getNumAttemptedMeasurements(), |
| RangingRequest.getMaxRttBurstSize()); |
| assertTrue("Wi-Fi RTT results: should be a 802.11MC measurement", |
| result.is80211mcMeasurement()); |
| } |
| 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(); |
| numAttempted[i - numFailures] = result.getNumAttemptedMeasurements(); |
| numSuccessful[i - numFailures] = result.getNumSuccessfulMeasurements(); |
| timestampsMs[i - numFailures] = result.getRangingTimestampMillis(); |
| |
| byte[] currentLci = result.getLci(); |
| byte[] currentLcr = result.getLcr(); |
| if (i - numFailures > 0) { |
| assertArrayEquals( |
| "Wi-Fi RTT results: invalid result (LCI mismatch) on iteration " + i, |
| currentLci, lastLci); |
| assertArrayEquals( |
| "Wi-Fi RTT results: invalid result (LCR mismatch) on iteration " + i, |
| currentLcr, lastLcr); |
| } |
| lastLci = currentLci; |
| lastLcr = currentLcr; |
| } else { |
| numFailures++; |
| } |
| // Sleep a while to avoid stress AP. |
| Thread.sleep(INTERVAL_MS); |
| } |
| |
| // 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.addValues("num_attempted", Arrays.copyOf(numAttempted, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.addValues("num_successful", Arrays.copyOf(numSuccessful, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.addValues("timestamps", Arrays.copyOf(timestampsMs, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.submit(); |
| |
| // Analyze results |
| assertTrue("Wi-Fi RTT failure rate exceeds threshold: FAIL=" + numFailures + ", ITERATIONS=" |
| + NUM_OF_RTT_ITERATIONS + ", AP=" + testAp, |
| numFailures <= NUM_OF_RTT_ITERATIONS * MAX_FAILURE_RATE_PERCENT / 100); |
| if (numFailures != NUM_OF_RTT_ITERATIONS) { |
| double distanceAvg = (double) distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures); |
| assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold, Variation =" |
| + (distanceMax - distanceAvg), |
| (distanceMax - distanceAvg) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM); |
| assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold, Variation =" |
| + (distanceAvg - distanceMin), |
| (distanceAvg - distanceMin) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM); |
| for (int i = 0; i < numGoodResults; ++i) { |
| assertNotEquals("Number of attempted measurements is 0", 0, numAttempted[i]); |
| assertNotEquals("Number of successful measurements is 0", 0, numSuccessful[i]); |
| } |
| } |
| } |
| |
| |
| |
| /** |
| * Validate that when a request contains more range operations than allowed (by API) that we |
| * get an exception. |
| */ |
| @Test |
| public void testRequestTooLarge() throws InterruptedException { |
| ScanResult testAp = getS11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that " |
| + "your test setup includes them!", testAp); |
| |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| List<ScanResult> scanResults = new ArrayList<>(); |
| for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) { |
| scanResults.add(testAp); |
| } |
| builder.addAccessPoints(scanResults); |
| |
| ScanResult testApNon80211mc = null; |
| if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| testApNon80211mc = getNone11McScanResult(); |
| } |
| if (testApNon80211mc == null) { |
| builder.addAccessPoints(List.of(testAp, testAp, testAp)); |
| } else { |
| builder.addNon80211mcCapableAccessPoints(List.of(testApNon80211mc, testApNon80211mc, |
| testApNon80211mc)); |
| } |
| |
| try { |
| mWifiRttManager.startRanging(builder.build(), mExecutor, new ResultCallback()); |
| } catch (IllegalArgumentException e) { |
| return; |
| } |
| |
| fail("Did not receive expected IllegalArgumentException when tried to range to too " |
| + "many peers"); |
| } |
| |
| /** |
| * Verify ResponderLocation API |
| */ |
| @Test |
| public void testRangingToTestApWithResponderLocation() throws InterruptedException { |
| // Scan for IEEE 802.11mc supporting APs |
| ScanResult testAp = getS11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that " |
| + "your test setup includes them!", testAp); |
| |
| // Perform RTT operations |
| RangingRequest request = new RangingRequest.Builder().addAccessPoint(testAp).build(); |
| ResultCallback callback = new ResultCallback(); |
| mWifiRttManager.startRanging(request, mExecutor, callback); |
| assertTrue("Wi-Fi RTT results: no callback! ", |
| callback.waitForCallback()); |
| |
| RangingResult result = callback.getResults().get(0); |
| assertEquals("Ranging request not success", |
| result.getStatus(), RangingResult.STATUS_SUCCESS); |
| ResponderLocation responderLocation = result.getUnverifiedResponderLocation(); |
| if (responderLocation == null) { |
| return; |
| } |
| assertTrue("ResponderLocation is not valid", responderLocation.isLciSubelementValid()); |
| |
| // Check LCI related APIs |
| int exceptionCount = 0; |
| int apiCount = 0; |
| try { |
| apiCount++; |
| responderLocation.getLatitudeUncertainty(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getLatitude(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getLongitudeUncertainty(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getLongitude(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getAltitudeType(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getAltitudeUncertainty(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getAltitude(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getDatum(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getRegisteredLocationAgreementIndication(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| responderLocation.getLciVersion(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| try { |
| apiCount++; |
| assertNotNull(responderLocation.toLocation()); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| // If LCI is not valid, all APIs should throw exception, otherwise no exception. |
| assertEquals("Exception number should equal to API number", |
| responderLocation.isLciSubelementValid()? 0 : apiCount, exceptionCount); |
| |
| // Verify ZaxisSubelement APIs |
| apiCount = 0; |
| exceptionCount = 0; |
| |
| try { |
| apiCount++; |
| responderLocation.getExpectedToMove(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| |
| try { |
| apiCount++; |
| responderLocation.getFloorNumber(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| |
| try { |
| apiCount++; |
| responderLocation.getHeightAboveFloorMeters(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| |
| try { |
| apiCount++; |
| responderLocation.getHeightAboveFloorUncertaintyMeters(); |
| } catch (IllegalStateException e) { |
| exceptionCount++; |
| } |
| // If Zaxis is not valid, all APIs should throw exception, otherwise no exception. |
| assertEquals("Exception number should equal to API number", |
| responderLocation.isZaxisSubelementValid() ? 0 : apiCount, exceptionCount); |
| // Verify civic location |
| if (responderLocation.toCivicLocationAddress() == null) { |
| assertNull(responderLocation.toCivicLocationSparseArray()); |
| } else { |
| assertNotNull(responderLocation.toCivicLocationSparseArray()); |
| } |
| // Verify map image |
| if (responderLocation.getMapImageUri() == null) { |
| assertNull(responderLocation.getMapImageMimeType()); |
| } else { |
| assertNotNull(responderLocation.getMapImageMimeType()); |
| } |
| boolean extraInfoOnAssociationIndication = |
| responderLocation.getExtraInfoOnAssociationIndication(); |
| assertNotNull("ColocatedBSSID list should be nonNull", |
| responderLocation.getColocatedBssids()); |
| } |
| |
| /** |
| * Verify ranging request with aware peer Mac address and peer handle. |
| */ |
| @Test |
| public void testAwareRttWithMacAddress() throws InterruptedException { |
| if (!WifiFeature.isAwareSupported(getContext())) { |
| return; |
| } |
| RangingRequest request = new RangingRequest.Builder() |
| .addWifiAwarePeer(MAC).build(); |
| ResultCallback callback = new ResultCallback(); |
| mWifiRttManager.startRanging(request, mExecutor, callback); |
| assertTrue("Wi-Fi RTT results: no callback", |
| callback.waitForCallback()); |
| List<RangingResult> rangingResults = callback.getResults(); |
| assertNotNull("Wi-Fi RTT results: null results", rangingResults); |
| assertEquals(1, rangingResults.size()); |
| assertEquals(RangingResult.STATUS_FAIL, rangingResults.get(0).getStatus()); |
| } |
| |
| /** |
| * Verify ranging request with aware peer handle. |
| */ |
| @Test |
| public void testAwareRttWithPeerHandle() throws InterruptedException { |
| if (WifiFeature.isAwareSupported(getContext())) { |
| return; |
| } |
| PeerHandle peerHandle = mock(PeerHandle.class); |
| RangingRequest request = new RangingRequest.Builder() |
| .addWifiAwarePeer(peerHandle).build(); |
| ResultCallback callback = new ResultCallback(); |
| mWifiRttManager.startRanging(request, mExecutor, callback); |
| assertTrue("Wi-Fi RTT results: no callback", |
| callback.waitForCallback()); |
| List<RangingResult> rangingResults = callback.getResults(); |
| assertNotNull("Wi-Fi RTT results: null results", rangingResults); |
| assertEquals("Invalid peerHandle should return 0 result", 0, rangingResults.size()); |
| } |
| |
| /** |
| * Test Wi-Fi One-sided RTT ranging operation using ScanResult in request: |
| * - Scan for visible APs for the test AP (which do not support IEEE 802.11mc) and are operating |
| * - in the 5GHz band. |
| * - Perform N (constant) RTT operations |
| * - Remove outliers while insuring greater than 50% of the results still remain |
| * - Validate: |
| * - Failure ratio < threshold (constant) |
| * - Result margin < threshold (constant) |
| */ |
| @Test |
| public void testRangingToTestNon11mcApUsingScanResult() throws InterruptedException { |
| if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| return; |
| } |
| |
| // Scan for Non-IEEE 802.11mc supporting APs |
| ScanResult testAp = getNone11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which are Non-IEEE 802.11mc - please verify that" |
| + " your test setup includes them!", testAp); |
| |
| // Perform RTT operations |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| builder.addNon80211mcCapableAccessPoint(testAp); |
| builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); |
| RangingRequest request = builder.build(); |
| |
| // Perform the request |
| rangeNon11mcApRequest(request, testAp, MAX_NON11MC_VARIATION_FROM_AVERAGE_DISTANCE_MM); |
| } |
| |
| /** |
| * Test Wi-Fi one-sided RTT ranging operation using ResponderConfig in request: |
| * - Scan for visible APs for the test AP (which do not support IEEE 802.11mc) and are operating |
| * - in the 5GHz band. |
| * - Perform N (constant) RTT operations |
| * - Remove outliers while insuring greater than 50% of the results still remain |
| * - Validate: |
| * - Failure ratio < threshold (constant) |
| * - Result margin < threshold (constant) |
| */ |
| @Test |
| public void testRangingToTestNon11mcApUsingResponderConfig() throws InterruptedException { |
| if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) { |
| return; |
| } |
| |
| // Scan for Non-IEEE 802.11mc supporting APs |
| ScanResult testAp = getNone11McScanResult(); |
| assertNotNull( |
| "Cannot find any test APs which are Non-IEEE 802.11mc - please verify that" |
| + " your test setup includes them!", testAp); |
| |
| ResponderConfig responder = ResponderConfig.fromScanResult(testAp); |
| |
| // Perform RTT operations |
| RangingRequest.Builder builder = new RangingRequest.Builder(); |
| builder.addResponder(responder); |
| builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); |
| RangingRequest request = builder.build(); |
| |
| |
| |
| // Perform the request |
| rangeNon11mcApRequest(request, testAp, MAX_NON11MC_VARIATION_FROM_AVERAGE_DISTANCE_MM); |
| } |
| |
| /** |
| * Utility method for validating a ranging request to a non-80211mc AP. |
| * |
| * @param request the ranging request that is being tested |
| * @param testAp the original test scan result to provide feedback on failure conditions |
| */ |
| private void rangeNon11mcApRequest(RangingRequest request, ScanResult testAp, |
| int variationLimit) throws InterruptedException { |
| Thread.sleep(5000); |
| 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]; |
| boolean[] distanceInclusionMap = new boolean[NUM_OF_RTT_ITERATIONS]; |
| int[] distanceStdDevMms = new int[NUM_OF_RTT_ITERATIONS]; |
| int[] rssis = new int[NUM_OF_RTT_ITERATIONS]; |
| int[] numAttempted = new int[NUM_OF_RTT_ITERATIONS]; |
| int[] numSuccessful = new int[NUM_OF_RTT_ITERATIONS]; |
| long[] timestampsMs = new long[NUM_OF_RTT_ITERATIONS]; |
| byte[] lastLci = null; |
| byte[] lastLcr = null; |
| 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(); |
| assertNotNull( |
| "Wi-Fi RTT results: null results (onRangingFailure) on iteration " + i, |
| currentResults); |
| assertEquals( |
| "Wi-Fi RTT results: unexpected # of results (expect 1) on iteration " + i, |
| 1, currentResults.size()); |
| RangingResult result = currentResults.get(0); |
| assertEquals( |
| "Wi-Fi RTT results: invalid result (wrong BSSID) entry on iteration " + i, |
| result.getMacAddress().toString(), testAp.BSSID); |
| |
| assertNull( |
| "Wi-Fi RTT results: invalid result (non-null PeerHandle) entry on iteration " |
| + i, result.getPeerHandle()); |
| |
| allResults.add(result); |
| int status = result.getStatus(); |
| statuses[i] = status; |
| if (status == RangingResult.STATUS_SUCCESS) { |
| assertFalse("Wi-Fi RTT results: should not be a 802.11MC measurement", |
| result.is80211mcMeasurement()); |
| distanceSum += 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(); |
| // For one-sided RTT the number of packets attempted in a burst is not available, |
| // So we set the result to be the same as used in the request. |
| numAttempted[i - numFailures] = request.getRttBurstSize(); |
| numSuccessful[i - numFailures] = result.getNumSuccessfulMeasurements(); |
| timestampsMs[i - numFailures] = result.getRangingTimestampMillis(); |
| |
| byte[] currentLci = result.getLci(); |
| byte[] currentLcr = result.getLcr(); |
| if (i - numFailures > 0) { |
| assertArrayEquals( |
| "Wi-Fi RTT results: invalid result (LCI mismatch) on iteration " + i, |
| currentLci, lastLci); |
| assertArrayEquals( |
| "Wi-Fi RTT results: invalid result (LCR mismatch) on iteration " + i, |
| currentLcr, lastLcr); |
| } |
| lastLci = currentLci; |
| lastLcr = currentLcr; |
| } else { |
| numFailures++; |
| } |
| // Sleep a while to avoid stress AP. |
| Thread.sleep(INTERVAL_MS); |
| } |
| // 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.addValues("num_attempted", Arrays.copyOf(numAttempted, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.addValues("num_successful", Arrays.copyOf(numSuccessful, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.addValues("timestamps", Arrays.copyOf(timestampsMs, numGoodResults), |
| ResultType.NEUTRAL, ResultUnit.NONE); |
| reportLog.submit(); |
| |
| /** TODO(b/237011062): enable the performance verification new API to check capabilities |
| // Analyze results |
| assertTrue("Wi-Fi RTT failure rate exceeds threshold: FAIL=" + numFailures |
| + ", ITERATIONS=" |
| + NUM_OF_RTT_ITERATIONS + ", AP=" + testAp, |
| numFailures <= NUM_OF_RTT_ITERATIONS * MAX_NON11MC_FAILURE_RATE_PERCENT / 100); |
| */ |
| |
| if (numFailures != NUM_OF_RTT_ITERATIONS) { |
| // Calculate an initial average using all measurements to determine distance outliers |
| double distanceAvg = (double) distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures); |
| // Now figure out the distance outliers and mark them in the distance inclusion map |
| int validDistances = 0; |
| for (int i = 0; i < (NUM_OF_RTT_ITERATIONS - numFailures); i++) { |
| if (distanceMms[i] - variationLimit < distanceAvg) { |
| // Distances that are in range for the distribution are included in the map |
| distanceInclusionMap[i] = true; |
| validDistances++; |
| } else { |
| // Distances that are out of range for the distribution are excluded in the map |
| distanceInclusionMap[i] = false; |
| } |
| } |
| |
| assertTrue("After fails+outlier removal greater that 50% distances must remain: " |
| + NUM_OF_RTT_ITERATIONS / 2, validDistances > NUM_OF_RTT_ITERATIONS / 2); |
| |
| // Remove the distance outliers and find the new average, min and max. |
| distanceSum = 0; |
| distanceMax = Integer.MIN_VALUE; |
| distanceMin = Integer.MAX_VALUE; |
| for (int i = 0; i < (NUM_OF_RTT_ITERATIONS - numFailures); i++) { |
| if (distanceInclusionMap[i]) { |
| distanceSum += distanceMms[i]; |
| distanceMin = Math.min(distanceMin, distanceMms[i]); |
| distanceMax = Math.max(distanceMax, distanceMms[i]); |
| } |
| } |
| distanceAvg = (double) distanceSum / validDistances; |
| assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold, Variation =" |
| + (distanceMax - distanceAvg), |
| (distanceMax - distanceAvg) <= variationLimit); |
| assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold, Variation =" |
| + (distanceAvg - distanceMin), |
| (distanceAvg - distanceMin) <= variationLimit); |
| for (int i = 0; i < numGoodResults; ++i) { |
| assertNotEquals("Number of attempted measurements is 0", 0, numAttempted[i]); |
| assertNotEquals("Number of successful measurements is 0", 0, numSuccessful[i]); |
| } |
| } |
| |
| } |
| } |