/* * Copyright (C) 2017 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; import android.annotation.ElapsedRealtimeLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.ParcelUtil; import android.net.wifi.ScanResult; import android.net.wifi.WifiAnnotations.ChannelWidth; import android.net.wifi.aware.PeerHandle; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Ranging result for a request started by * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}. * Results are returned in {@link RangingResultCallback#onRangingResults(List)}. *
* A ranging result is the distance measurement result for a single device specified in the * {@link RangingRequest}. */ public final class RangingResult implements Parcelable { private static final String TAG = "RangingResult"; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** @hide */ @IntDef({STATUS_SUCCESS, STATUS_FAIL, STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}) @Retention(RetentionPolicy.SOURCE) public @interface RangeResultStatus { } /** * Individual range request status, {@link #getStatus()}. Indicates ranging operation was * successful and distance value is valid. */ public static final int STATUS_SUCCESS = 0; /** * Individual range request status, {@link #getStatus()}. Indicates ranging operation failed * and the distance value is invalid. */ public static final int STATUS_FAIL = 1; /** * Individual range request status, {@link #getStatus()}. Indicates that the ranging operation * failed because the specified peer does not support IEEE 802.11mc RTT operations. Support by * an Access Point can be confirmed using * {@link android.net.wifi.ScanResult#is80211mcResponder()}. *
* On such a failure, the individual result fields of {@link RangingResult} such as
* {@link RangingResult#getDistanceMm()} are invalid.
*/
public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2;
/**
* The unspecified value.
*/
public static final int UNSPECIFIED = -1;
private final @RangeResultStatus 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;
private final boolean mIs80211mcMeasurement;
private final int mFrequencyMHz;
private final int mPacketBw;
private final boolean mIs80211azNtbMeasurement;
private final long mNtbMinMeasurementTime;
private final long mNtbMaxMeasurementTime;
private final int mI2rTxLtfRepetitions;
private final int mR2iTxLtfRepetitions;
private final int mNumTxSpatialStreams;
private final int mNumRxSpatialStreams;
private List
* Will return a {@code null} for results corresponding to requests issued using a {@code
* PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API.
*/
@Nullable
public MacAddress getMacAddress() {
return mMac;
}
/**
* @return The PeerHandle of the device whose reange measurement was requested. Will correspond
* to the PeerHandle of the devices requested using
* {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)}.
*
* Will return a {@code null} for results corresponding to requests issued using a MAC address.
*/
@Nullable public PeerHandle getPeerHandle() {
return mPeerHandle;
}
/**
* @return The distance (in mm) to the device specified by {@link #getMacAddress()} or
* {@link #getPeerHandle()}.
*
* Note: the measured distance may be negative for very close devices.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public int getDistanceMm() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getDistanceMm(): invoked on an invalid result: getStatus()=" + mStatus);
}
return mDistanceMm;
}
/**
* @return The standard deviation of the measured distance (in mm) to the device specified by
* {@link #getMacAddress()} or {@link #getPeerHandle()}. The standard deviation is calculated
* over the measurements executed in a single RTT burst. The number of measurements is returned
* by {@link #getNumSuccessfulMeasurements()} - 0 successful measurements indicate that the
* standard deviation is not valid (a valid standard deviation requires at least 2 data points).
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public int getDistanceStdDevMm() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getDistanceStdDevMm(): invoked on an invalid result: getStatus()=" + mStatus);
}
return mDistanceStdDevMm;
}
/**
* @return The average RSSI, in units of dBm, observed during the RTT measurement.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public int getRssi() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getRssi(): invoked on an invalid result: getStatus()=" + mStatus);
}
return mRssi;
}
/**
* @return The number of attempted measurements used in the RTT exchange resulting in this set
* of results. The number of successful measurements is returned by
* {@link #getNumSuccessfulMeasurements()} which at most, if there are no errors, will be 1
* less than the number of attempted measurements.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, 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) {
throw new IllegalStateException(
"getNumAttemptedMeasurements(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mNumAttemptedMeasurements;
}
/**
* @return The number of successful measurements used to calculate the distance and standard
* deviation. If the number of successful measurements if 1 then then standard deviation,
* returned by {@link #getDistanceStdDevMm()}, is not valid (a 0 is returned for the standard
* deviation).
*
* The total number of measurement attempts is returned by
* {@link #getNumAttemptedMeasurements()}. The number of successful measurements will be at
* most 1 less then the number of attempted measurements.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public int getNumSuccessfulMeasurements() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getNumSuccessfulMeasurements(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mNumSuccessfulMeasurements;
}
/**
* @return The unverified responder location represented as {@link ResponderLocation} which
* captures location information the responder is programmed to broadcast. The responder
* location is referred to as unverified, because we are relying on the device/site
* administrator to correctly configure its location data.
*
* Will return a {@code null} when the location information cannot be parsed.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
@Nullable
public ResponderLocation getUnverifiedResponderLocation() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getUnverifiedResponderLocation(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mResponderLocation;
}
/**
* @return The Location Configuration Information (LCI) as self-reported by the peer. The format
* is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.10.
*
* Note: the information is NOT validated - use with caution. Consider validating it with
* other sources of information before using it.
*/
@SuppressLint("UnflaggedApi") // Flagging API promotion from @SystemApi to public not supported
@NonNull
public byte[] getLci() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getLci(): invoked on an invalid result: getStatus()=" + mStatus);
}
return mLci;
}
/**
* @return The Location Civic report (LCR) as self-reported by the peer. The format
* is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.13.
*
* Note: the information is NOT validated - use with caution. Consider validating it with
* other sources of information before using it.
*/
@SuppressLint("UnflaggedApi") // Flagging API promotion from @SystemApi to public not supported
@NonNull
public byte[] getLcr() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getReportedLocationCivic(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mLcr;
}
/**
* @return The timestamp at which the ranging operation was performed. The timestamp is in
* milliseconds since boot, including time spent in sleep, corresponding to values provided by
* {@link android.os.SystemClock#elapsedRealtime()}.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public long getRangingTimestampMillis() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getRangingTimestampMillis(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mTimestamp;
}
/**
* @return The result is true if the IEEE 802.11mc protocol was used. If the result is false,
* and {@link #is80211azNtbMeasurement()} is also false a one-side RTT result is provided
* which does not subtract the turnaround time at the responder.
*
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
public boolean is80211mcMeasurement() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"is80211mcMeasurementResult(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mIs80211mcMeasurement;
}
/**
* @return The result is true if the IEEE 802.11az non-trigger based protocol was used. If the
* result is false, and {@link #is80211mcMeasurement()} is also false a one-side RTT result
* is provided which does not subtract the turnaround time at the responder.
* .
* Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
* exception.
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public boolean is80211azNtbMeasurement() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"is80211azNtbMeasurement(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mIs80211azNtbMeasurement;
}
/**
* Gets minimum time between measurements in microseconds for IEEE 802.11az non-trigger based
* ranging.
*
* The next 11az ranging measurement request must be invoked after the minimum time from the
* last measurement time {@link #getRangingTimestampMillis()} for the peer. Otherwise, cached
* ranging result will be returned for the peer.
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public long getMinTimeBetweenNtbMeasurementsMicros() {
return mNtbMinMeasurementTime;
}
/**
* Gets maximum time between measurements in microseconds for IEEE 802.11az non-trigger based
* ranging.
*
* The next 11az ranging request needs to be invoked before the maximum time from the last
* measurement time {@link #getRangingTimestampMillis()}. Otherwise, the non-trigger based
* ranging session will be terminated and a new ranging negotiation will happen with
* the responding station.
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public long getMaxTimeBetweenNtbMeasurementsMicros() {
return mNtbMaxMeasurementTime;
}
/**
* Gets LTF repetitions that the responder station (RSTA) used in the preamble of the
* responder to initiator (I2R) null data PPDU (NDP) for this result.
*
* LTF repetitions is the multiple transmissions of HE-LTF symbols in an HE ranging NDP. An
* HE-LTF repetition value of 1 indicates no repetitions.
*
* @return LTF repetitions count
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public int get80211azResponderTxLtfRepetitionsCount() {
return mR2iTxLtfRepetitions;
}
/**
* Gets LTF repetitions that the initiator station (ISTA) used in the preamble of the
* initiator to responder (I2R) null data PPDU (NDP) for this result.
*
* LTF repetitions is the multiple transmissions of HE-LTF symbols in an HE ranging NDP. An
* HE-LTF repetition value of 1 indicates no repetitions.
*
* @return LTF repetitions count
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public int get80211azInitiatorTxLtfRepetitionsCount() {
return mI2rTxLtfRepetitions;
}
/**
* Gets number of transmit spatial streams that the initiator station (ISTA) used for the
* initiator to responder (I2R) null data PPDU (NDP) for this result.
*
* @return Number of spatial streams
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public int get80211azNumberOfTxSpatialStreams() {
return mNumTxSpatialStreams;
}
/**
* Gets number of receive spatial streams that the initiator station (ISTA) used for the
* initiator to responder (I2R) null data PPDU (NDP) for this result.
*
* @return Number of spatial streams
*/
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
public int get80211azNumberOfRxSpatialStreams() {
return mNumRxSpatialStreams;
}
/**
* The center frequency of the primary 20 MHz frequency (in MHz) of the channel over
* which the measurement frames are sent.
* @return center frequency in Mhz of the channel if available, otherwise {@link #UNSPECIFIED}
* is returned.
*
* @throws IllegalStateException if {@link #getStatus()} does not return
* {@link #STATUS_SUCCESS}.
*/
public int getMeasurementChannelFrequencyMHz() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getMeasurementChannelFrequencyMHz():"
+ " invoked on an invalid result: getStatus()= " + mStatus);
}
return mFrequencyMHz;
}
/**
* The bandwidth used to transmit the RTT measurement frame.
* @return one of {@link ScanResult#CHANNEL_WIDTH_20MHZ},
* {@link ScanResult#CHANNEL_WIDTH_40MHZ},
* {@link ScanResult#CHANNEL_WIDTH_80MHZ}, {@link ScanResult#CHANNEL_WIDTH_160MHZ},
* {@link ScanResult #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link ScanResult #CHANNEL_WIDTH_320MHZ}
* if available, otherwise {@link #UNSPECIFIED} is returned.
*
* @throws IllegalStateException if {@link #getStatus()} does not return
* {@link #STATUS_SUCCESS}.
*/
public @ChannelWidth int getMeasurementBandwidth() {
if (mStatus != STATUS_SUCCESS) {
throw new IllegalStateException(
"getMeasurementBandwidth(): invoked on an invalid result: getStatus()="
+ mStatus);
}
return mPacketBw;
}
/**
* Get the vendor-provided configuration data, if it exists.
*
* @return Vendor configuration data, or empty list if it does not exist.
* @hide
*/
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
@SystemApi
@NonNull
public List