Updated API for one-sided RTT ranging operation (client)
Bug: 163335690
Test: all unit tests continue to pass. Two new tests added.
Change-Id: I01d46af84e3b7e01641af1082b6cc3332cd7525b
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 196566b..9474d97 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -1176,6 +1176,8 @@
ctor public RangingRequest.Builder();
method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoint(@NonNull android.net.wifi.ScanResult);
method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoints(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method @NonNull public android.net.wifi.rtt.RangingRequest.Builder addNon80211mcCapableAccessPoint(@NonNull android.net.wifi.ScanResult);
+ method @NonNull public android.net.wifi.rtt.RangingRequest.Builder addNon80211mcCapableAccessPoints(@NonNull java.util.List<android.net.wifi.ScanResult>);
method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.MacAddress);
method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.wifi.aware.PeerHandle);
method public android.net.wifi.rtt.RangingRequest build();
@@ -1194,6 +1196,7 @@
method public int getRssi();
method public int getStatus();
method @Nullable public android.net.wifi.rtt.ResponderLocation getUnverifiedResponderLocation();
+ method public boolean is80211mcMeasurement();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR;
field public static final int STATUS_FAIL = 1; // 0x1
diff --git a/framework/java/android/net/wifi/rtt/RangingRequest.java b/framework/java/android/net/wifi/rtt/RangingRequest.java
index 04dfcf2..101b1d2 100644
--- a/framework/java/android/net/wifi/rtt/RangingRequest.java
+++ b/framework/java/android/net/wifi/rtt/RangingRequest.java
@@ -233,9 +233,9 @@
* which to measure range. The total number of peers added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
* <p>
- * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
- * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
- * not supported the result status will be
+ * Ranging will only be supported if the Access Point supports IEEE 802.11mc, also known as
+ * two-sided RTT. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point's
+ * capabilities. If not supported the result status will be
* {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
*
* @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
@@ -254,9 +254,9 @@
* which to measure range. The total number of peers added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
* <p>
- * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
- * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
- * not supported the result status will be
+ * Ranging will only be supported if the Access Point supports IEEE 802.11mc, also known as
+ * two-sided RTT. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point's
+ * capabilities. If not supported, the result status will be
* {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
*
* @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
@@ -274,6 +274,86 @@
}
/**
+ * Add the non-802.11mc capable device specified by the {@link ScanResult} to the list of
+ * devices with which to measure range. The total number of peers added to a request cannot
+ * exceed the limit specified by {@link #getMaxPeers()}.
+ * <p>
+ * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc,
+ * and instead an alternate protocol called one-sided RTT will be used with lower
+ * accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point)s) are
+ * not 802.11mc capable.
+ * <p>
+ * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can
+ * add hundreds of meters to the estimate. With experimentation it is possible to use this
+ * information to make a statistical estimate of the range by taking multiple measurements
+ * to several Access Points and normalizing the result. For some applications this can be
+ * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but
+ * will not be as accurate as IEEE 802.11mc (two-sided RTT).
+ * <p>
+ * Note: one-sided RTT should only be used if you are very familiar with statistical
+ * estimation techniques.
+ *
+ * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ @NonNull
+ public Builder addNon80211mcCapableAccessPoint(@NonNull ScanResult apInfo) {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ if (apInfo == null) {
+ throw new IllegalArgumentException("Null ScanResult!");
+ }
+ if (apInfo.is80211mcResponder()) {
+ throw new IllegalArgumentException("AP supports the 802.11mc protocol.");
+ }
+ return addResponder(ResponderConfig.fromScanResult(apInfo));
+ }
+
+ /**
+ * Add the non-802.11mc capable devices specified by the {@link ScanResult} to the list of
+ * devices with which to measure range. The total number of peers added to a request cannot
+ * exceed the limit specified by {@link #getMaxPeers()}.
+ * <p>
+ * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc,
+ * and instead an alternate protocol called one-sided RTT will be used with lower
+ * accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point)s) are
+ * not 802.11mc capable.
+ * <p>
+ * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can
+ * add hundreds of meters to the estimate. With experimentation it is possible to use this
+ * information to make a statistical estimate of the range by taking multiple measurements
+ * to several Access Points and normalizing the result. For some applications this can be
+ * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but
+ * will not be as accurate as IEEE 802.11mc (two-sided RTT).
+ * <p>
+ * Note: one-sided RTT should only be used if you are very familiar with statistical
+ * estimation techniques.
+ *
+ * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ @NonNull
+ public Builder addNon80211mcCapableAccessPoints(@NonNull List<ScanResult> apInfos) {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ if (apInfos == null) {
+ throw new IllegalArgumentException("Null list of ScanResults!");
+ }
+ for (ScanResult scanResult : apInfos) {
+ if (scanResult.is80211mcResponder()) {
+ throw new IllegalArgumentException(
+ "At least one AP supports the 802.11mc protocol.");
+ }
+ addAccessPoint(scanResult);
+ }
+ return this;
+ }
+
+ /**
* Add the device specified by the {@code peerMacAddress} to the list of devices with
* which to measure range.
* <p>
diff --git a/framework/java/android/net/wifi/rtt/RangingResult.java b/framework/java/android/net/wifi/rtt/RangingResult.java
index a065bbc..e2f270c 100644
--- a/framework/java/android/net/wifi/rtt/RangingResult.java
+++ b/framework/java/android/net/wifi/rtt/RangingResult.java
@@ -25,6 +25,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.modules.utils.build.SdkLevel;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -72,24 +74,50 @@
*/
public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2;
- private final int mStatus;
- private final MacAddress mMac;
- private final PeerHandle mPeerHandle;
- private final int mDistanceMm;
- private final int mDistanceStdDevMm;
- private final int mRssi;
- private final int mNumAttemptedMeasurements;
- private final int mNumSuccessfulMeasurements;
- private final byte[] mLci;
- private final byte[] mLcr;
- private final ResponderLocation mResponderLocation;
- private final long mTimestamp;
+ /** @hide */
+ public final int mStatus;
+
+ /** @hide */
+ public final MacAddress mMac;
+
+ /** @hide */
+ public final PeerHandle mPeerHandle;
+
+ /** @hide */
+ public final int mDistanceMm;
+
+ /** @hide */
+ public final int mDistanceStdDevMm;
+
+ /** @hide */
+ public final int mRssi;
+
+ /** @hide */
+ public final int mNumAttemptedMeasurements;
+
+ /** @hide */
+ public final int mNumSuccessfulMeasurements;
+
+ /** @hide */
+ public final byte[] mLci;
+
+ /** @hide */
+ public final byte[] mLcr;
+
+ /** @hide */
+ public final ResponderLocation mResponderLocation;
+
+ /** @hide */
+ public final long mTimestamp;
+
+ /** @hide */
+ public final boolean mIs80211mcMeasurement;
/** @hide */
public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
int numSuccessfulMeasurements, byte[] lci, byte[] lcr,
- ResponderLocation responderLocation, long timestamp) {
+ ResponderLocation responderLocation, long timestamp, boolean is80211McMeasurement) {
mStatus = status;
mMac = mac;
mPeerHandle = null;
@@ -102,6 +130,7 @@
mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
mResponderLocation = responderLocation;
mTimestamp = timestamp;
+ mIs80211mcMeasurement = is80211McMeasurement;
}
/** @hide */
@@ -121,6 +150,7 @@
mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
mResponderLocation = responderLocation;
mTimestamp = timestamp;
+ mIs80211mcMeasurement = true;
}
/**
@@ -210,8 +240,10 @@
* {@link #getNumSuccessfulMeasurements()} which at most, if there are no errors, will be 1 less
* that the number of attempted measurements.
* <p>
- * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
- * exception.
+ * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, and if the method
+ * {@link #is80211mcMeasurement()} returns true, otherwise will throw an exception. If the value
+ * is 0, it should be interpreted as no information available, which may occur for one-sided RTT
+ * measurements. Instead {@link RangingRequest#getRttBurstSize()} should be used instead.
*/
public int getNumAttemptedMeasurements() {
if (mStatus != STATUS_SUCCESS) {
@@ -321,6 +353,26 @@
return mTimestamp;
}
+ /**
+ * @return The result is true if the IEEE 802.11mc protocol was used (also known as
+ * two-sided RTT). If the result is false, a one-side RTT result is provided which does not
+ * subtract the turnaround time at the responder.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
+ * exception.
+ */
+ public boolean is80211mcMeasurement() {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ if (mStatus != STATUS_SUCCESS) {
+ throw new IllegalStateException(
+ "is80211mcMeasurementResult(): invoked on an invalid result: getStatus()="
+ + mStatus);
+ }
+ return mIs80211mcMeasurement;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -350,6 +402,7 @@
dest.writeByteArray(mLcr);
dest.writeParcelable(mResponderLocation, flags);
dest.writeLong(mTimestamp);
+ dest.writeBoolean(mIs80211mcMeasurement);
}
public static final @android.annotation.NonNull Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
@@ -381,6 +434,7 @@
ResponderLocation responderLocation =
in.readParcelable(this.getClass().getClassLoader());
long timestamp = in.readLong();
+ boolean isllmcMeasurement = in.readBoolean();
if (peerHandlePresent) {
return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr,
@@ -388,7 +442,7 @@
} else {
return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr,
- responderLocation, timestamp);
+ responderLocation, timestamp, isllmcMeasurement);
}
}
};
@@ -404,7 +458,8 @@
mNumAttemptedMeasurements).append(", numSuccessfulMeasurements=").append(
mNumSuccessfulMeasurements).append(", lci=").append(mLci).append(", lcr=").append(
mLcr).append(", responderLocation=").append(mResponderLocation)
- .append(", timestamp=").append(mTimestamp).append("]").toString();
+ .append(", timestamp=").append(mTimestamp).append(", is80211mcMeasurement=")
+ .append(mIs80211mcMeasurement).append("]").toString();
}
@Override
@@ -426,6 +481,7 @@
&& mNumSuccessfulMeasurements == lhs.mNumSuccessfulMeasurements
&& Arrays.equals(mLci, lhs.mLci) && Arrays.equals(mLcr, lhs.mLcr)
&& mTimestamp == lhs.mTimestamp
+ && mIs80211mcMeasurement == lhs.mIs80211mcMeasurement
&& Objects.equals(mResponderLocation, lhs.mResponderLocation);
}
@@ -433,6 +489,6 @@
public int hashCode() {
return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
mNumAttemptedMeasurements, mNumSuccessfulMeasurements, mLci, mLcr,
- mResponderLocation, mTimestamp);
+ mResponderLocation, mTimestamp, mIs80211mcMeasurement);
}
}
diff --git a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index c8006fe..b9f6217 100644
--- a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -85,7 +85,7 @@
List<RangingResult> results = new ArrayList<>();
results.add(
new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
- 10, 8, 5, null, null, null, 666));
+ 10, 8, 5, null, null, null, 666, true));
RangingResultCallback callbackMock = mock(RangingResultCallback.class);
ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
@@ -134,10 +134,13 @@
// Note: not validating parcel code of ScanResult (assumed to work)
ScanResult scanResult1 = new ScanResult();
scanResult1.BSSID = "00:01:02:03:04:05";
+ scanResult1.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
ScanResult scanResult2 = new ScanResult();
scanResult2.BSSID = "06:07:08:09:0A:0B";
+ scanResult2.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
ScanResult scanResult3 = new ScanResult();
scanResult3.BSSID = "AA:BB:CC:DD:EE:FF";
+ scanResult3.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
List<ScanResult> scanResults2and3 = new ArrayList<>(2);
scanResults2and3.add(scanResult2);
scanResults2and3.add(scanResult3);
@@ -169,9 +172,45 @@
* Validate the rtt burst size is set correctly when in range.
*/
@Test
+ public void test802llmcCapableAccessPointFailsForNon11mcBuilderMethods() {
+ ScanResult scanResult1 = new ScanResult();
+ scanResult1.BSSID = "AA:BB:CC:DD:EE:FF";
+ scanResult1.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+
+ // create request for one AP
+ try {
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addNon80211mcCapableAccessPoint(scanResult1);
+ fail("Single Access Point was 11mc capable.");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ ScanResult scanResult2 = new ScanResult();
+ scanResult2.BSSID = "11:BB:CC:DD:EE:FF";
+ scanResult2.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ List<ScanResult> scanResults = new ArrayList<>();
+ scanResults.add(scanResult1);
+ scanResults.add(scanResult2);
+
+ // create request for a list of 2 APs.
+ try {
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addNon80211mcCapableAccessPoints(scanResults);
+ fail("One Access Point in the List was 11mc capable.");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Validate the rtt burst size is set correctly when in range.
+ */
+ @Test
public void testRangingRequestSetBurstSize() {
ScanResult scanResult = new ScanResult();
scanResult.BSSID = "AA:BB:CC:DD:EE:FF";
+ scanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
// create request
RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -226,6 +265,7 @@
public void testRangingRequestAtLimit() {
ScanResult scanResult = new ScanResult();
scanResult.BSSID = "AA:BB:CC:DD:EE:FF";
+ scanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
List<ScanResult> scanResultList = new ArrayList<>();
for (int i = 0; i < RangingRequest.getMaxPeers() - 3; ++i) {
scanResultList.add(scanResult);
@@ -314,7 +354,8 @@
// RangingResults constructed with a MAC address
RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
- numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, null, timestamp);
+ numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, null, timestamp,
+ true);
Parcel parcelW = Parcel.obtain();
result.writeToParcel(parcelW, 0);
@@ -363,9 +404,11 @@
byte[] lcr = { };
RangingResult rr1 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
- numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, null, timestamp);
+ numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, null, timestamp,
+ true);
RangingResult rr2 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
- numAttemptedMeasurements, numSuccessfulMeasurements, null, null, null, timestamp);
+ numAttemptedMeasurements, numSuccessfulMeasurements, null, null, null, timestamp,
+ true);
assertEquals(rr1, rr2);
}