1 /* 2 * Copyright (C) 2017 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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.net.MacAddress; 24 import android.net.wifi.aware.PeerHandle; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Objects; 33 34 /** 35 * Ranging result for a request started by 36 * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}. 37 * Results are returned in {@link RangingResultCallback#onRangingResults(List)}. 38 * <p> 39 * A ranging result is the distance measurement result for a single device specified in the 40 * {@link RangingRequest}. 41 */ 42 public final class RangingResult implements Parcelable { 43 private static final String TAG = "RangingResult"; 44 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 45 46 /** @hide */ 47 @IntDef({STATUS_SUCCESS, STATUS_FAIL, STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}) 48 @Retention(RetentionPolicy.SOURCE) 49 public @interface RangeResultStatus { 50 } 51 52 /** 53 * Individual range request status, {@link #getStatus()}. Indicates ranging operation was 54 * successful and distance value is valid. 55 */ 56 public static final int STATUS_SUCCESS = 0; 57 58 /** 59 * Individual range request status, {@link #getStatus()}. Indicates ranging operation failed 60 * and the distance value is invalid. 61 */ 62 public static final int STATUS_FAIL = 1; 63 64 /** 65 * Individual range request status, {@link #getStatus()}. Indicates that the ranging operation 66 * failed because the specified peer does not support IEEE 802.11mc RTT operations. Support by 67 * an Access Point can be confirmed using 68 * {@link android.net.wifi.ScanResult#is80211mcResponder()}. 69 * <p> 70 * On such a failure, the individual result fields of {@link RangingResult} such as 71 * {@link RangingResult#getDistanceMm()} are invalid. 72 */ 73 public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; 74 75 /** @hide */ 76 public final int mStatus; 77 78 /** @hide */ 79 public final MacAddress mMac; 80 81 /** @hide */ 82 public final PeerHandle mPeerHandle; 83 84 /** @hide */ 85 public final int mDistanceMm; 86 87 /** @hide */ 88 public final int mDistanceStdDevMm; 89 90 /** @hide */ 91 public final int mRssi; 92 93 /** @hide */ 94 public final int mNumAttemptedMeasurements; 95 96 /** @hide */ 97 public final int mNumSuccessfulMeasurements; 98 99 /** @hide */ 100 public final byte[] mLci; 101 102 /** @hide */ 103 public final byte[] mLcr; 104 105 /** @hide */ 106 public final ResponderLocation mResponderLocation; 107 108 /** @hide */ 109 public final long mTimestamp; 110 111 /** @hide */ 112 public final boolean mIs80211mcMeasurement; 113 114 /** @hide */ RangingResult(@angeResultStatus int status, @NonNull MacAddress mac, int distanceMm, int distanceStdDevMm, int rssi, int numAttemptedMeasurements, int numSuccessfulMeasurements, byte[] lci, byte[] lcr, ResponderLocation responderLocation, long timestamp, boolean is80211McMeasurement)115 public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm, 116 int distanceStdDevMm, int rssi, int numAttemptedMeasurements, 117 int numSuccessfulMeasurements, byte[] lci, byte[] lcr, 118 ResponderLocation responderLocation, long timestamp, boolean is80211McMeasurement) { 119 mStatus = status; 120 mMac = mac; 121 mPeerHandle = null; 122 mDistanceMm = distanceMm; 123 mDistanceStdDevMm = distanceStdDevMm; 124 mRssi = rssi; 125 mNumAttemptedMeasurements = numAttemptedMeasurements; 126 mNumSuccessfulMeasurements = numSuccessfulMeasurements; 127 mLci = lci == null ? EMPTY_BYTE_ARRAY : lci; 128 mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr; 129 mResponderLocation = responderLocation; 130 mTimestamp = timestamp; 131 mIs80211mcMeasurement = is80211McMeasurement; 132 } 133 134 /** @hide */ RangingResult(@angeResultStatus int status, PeerHandle peerHandle, int distanceMm, int distanceStdDevMm, int rssi, int numAttemptedMeasurements, int numSuccessfulMeasurements, byte[] lci, byte[] lcr, ResponderLocation responderLocation, long timestamp)135 public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm, 136 int distanceStdDevMm, int rssi, int numAttemptedMeasurements, 137 int numSuccessfulMeasurements, byte[] lci, byte[] lcr, 138 ResponderLocation responderLocation, long timestamp) { 139 mStatus = status; 140 mMac = null; 141 mPeerHandle = peerHandle; 142 mDistanceMm = distanceMm; 143 mDistanceStdDevMm = distanceStdDevMm; 144 mRssi = rssi; 145 mNumAttemptedMeasurements = numAttemptedMeasurements; 146 mNumSuccessfulMeasurements = numSuccessfulMeasurements; 147 mLci = lci == null ? EMPTY_BYTE_ARRAY : lci; 148 mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr; 149 mResponderLocation = responderLocation; 150 mTimestamp = timestamp; 151 mIs80211mcMeasurement = true; 152 } 153 154 /** 155 * @return The status of ranging measurement: {@link #STATUS_SUCCESS} in case of success, and 156 * {@link #STATUS_FAIL} in case of failure. 157 */ 158 @RangeResultStatus getStatus()159 public int getStatus() { 160 return mStatus; 161 } 162 163 /** 164 * @return The MAC address of the device whose range measurement was requested. Will correspond 165 * to the MAC address of the device in the {@link RangingRequest}. 166 * <p> 167 * Will return a {@code null} for results corresponding to requests issued using a {@code 168 * PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API. 169 */ 170 @Nullable getMacAddress()171 public MacAddress getMacAddress() { 172 return mMac; 173 } 174 175 /** 176 * @return The PeerHandle of the device whose reange measurement was requested. Will correspond 177 * to the PeerHandle of the devices requested using 178 * {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)}. 179 * <p> 180 * Will return a {@code null} for results corresponding to requests issued using a MAC address. 181 */ getPeerHandle()182 @Nullable public PeerHandle getPeerHandle() { 183 return mPeerHandle; 184 } 185 186 /** 187 * @return The distance (in mm) to the device specified by {@link #getMacAddress()} or 188 * {@link #getPeerHandle()}. 189 * <p> 190 * Note: the measured distance may be negative for very close devices. 191 * <p> 192 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 193 * exception. 194 */ getDistanceMm()195 public int getDistanceMm() { 196 if (mStatus != STATUS_SUCCESS) { 197 throw new IllegalStateException( 198 "getDistanceMm(): invoked on an invalid result: getStatus()=" + mStatus); 199 } 200 return mDistanceMm; 201 } 202 203 /** 204 * @return The standard deviation of the measured distance (in mm) to the device specified by 205 * {@link #getMacAddress()} or {@link #getPeerHandle()}. The standard deviation is calculated 206 * over the measurements executed in a single RTT burst. The number of measurements is returned 207 * by {@link #getNumSuccessfulMeasurements()} - 0 successful measurements indicate that the 208 * standard deviation is not valid (a valid standard deviation requires at least 2 data points). 209 * <p> 210 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 211 * exception. 212 */ getDistanceStdDevMm()213 public int getDistanceStdDevMm() { 214 if (mStatus != STATUS_SUCCESS) { 215 throw new IllegalStateException( 216 "getDistanceStdDevMm(): invoked on an invalid result: getStatus()=" + mStatus); 217 } 218 return mDistanceStdDevMm; 219 } 220 221 /** 222 * @return The average RSSI, in units of dBm, observed during the RTT measurement. 223 * <p> 224 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 225 * exception. 226 */ getRssi()227 public int getRssi() { 228 if (mStatus != STATUS_SUCCESS) { 229 throw new IllegalStateException( 230 "getRssi(): invoked on an invalid result: getStatus()=" + mStatus); 231 } 232 return mRssi; 233 } 234 235 /** 236 * @return The number of attempted measurements used in the RTT exchange resulting in this set 237 * of results. The number of successful measurements is returned by 238 * {@link #getNumSuccessfulMeasurements()} which at most, if there are no errors, will be 1 239 * less than the number of attempted measurements. 240 * <p> 241 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 242 * exception. If the value is 0, it should be interpreted as no information available, which may 243 * occur for one-sided RTT measurements. Instead {@link RangingRequest#getRttBurstSize()} 244 * should be used instead. 245 */ getNumAttemptedMeasurements()246 public int getNumAttemptedMeasurements() { 247 if (mStatus != STATUS_SUCCESS) { 248 throw new IllegalStateException( 249 "getNumAttemptedMeasurements(): invoked on an invalid result: getStatus()=" 250 + mStatus); 251 } 252 return mNumAttemptedMeasurements; 253 } 254 255 /** 256 * @return The number of successful measurements used to calculate the distance and standard 257 * deviation. If the number of successful measurements if 1 then then standard deviation, 258 * returned by {@link #getDistanceStdDevMm()}, is not valid (a 0 is returned for the standard 259 * deviation). 260 * <p> 261 * The total number of measurement attempts is returned by 262 * {@link #getNumAttemptedMeasurements()}. The number of successful measurements will be at 263 * most 1 less then the number of attempted measurements. 264 * <p> 265 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 266 * exception. 267 */ getNumSuccessfulMeasurements()268 public int getNumSuccessfulMeasurements() { 269 if (mStatus != STATUS_SUCCESS) { 270 throw new IllegalStateException( 271 "getNumSuccessfulMeasurements(): invoked on an invalid result: getStatus()=" 272 + mStatus); 273 } 274 return mNumSuccessfulMeasurements; 275 } 276 277 /** 278 * @return The unverified responder location represented as {@link ResponderLocation} which 279 * captures location information the responder is programmed to broadcast. The responder 280 * location is referred to as unverified, because we are relying on the device/site 281 * administrator to correctly configure its location data. 282 * <p> 283 * Will return a {@code null} when the location information cannot be parsed. 284 * <p> 285 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 286 * exception. 287 */ 288 @Nullable getUnverifiedResponderLocation()289 public ResponderLocation getUnverifiedResponderLocation() { 290 if (mStatus != STATUS_SUCCESS) { 291 throw new IllegalStateException( 292 "getUnverifiedResponderLocation(): invoked on an invalid result: getStatus()=" 293 + mStatus); 294 } 295 return mResponderLocation; 296 } 297 298 /** 299 * @return The Location Configuration Information (LCI) as self-reported by the peer. The format 300 * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.10. 301 * <p> 302 * Note: the information is NOT validated - use with caution. Consider validating it with 303 * other sources of information before using it. 304 * 305 * @hide 306 */ 307 @SystemApi 308 @NonNull getLci()309 public byte[] getLci() { 310 if (mStatus != STATUS_SUCCESS) { 311 throw new IllegalStateException( 312 "getLci(): invoked on an invalid result: getStatus()=" + mStatus); 313 } 314 return mLci; 315 } 316 317 /** 318 * @return The Location Civic report (LCR) as self-reported by the peer. The format 319 * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.13. 320 * <p> 321 * Note: the information is NOT validated - use with caution. Consider validating it with 322 * other sources of information before using it. 323 * 324 * @hide 325 */ 326 @SystemApi 327 @NonNull getLcr()328 public byte[] getLcr() { 329 if (mStatus != STATUS_SUCCESS) { 330 throw new IllegalStateException( 331 "getReportedLocationCivic(): invoked on an invalid result: getStatus()=" 332 + mStatus); 333 } 334 return mLcr; 335 } 336 337 /** 338 * @return The timestamp at which the ranging operation was performed. The timestamp is in 339 * milliseconds since boot, including time spent in sleep, corresponding to values provided by 340 * {@link android.os.SystemClock#elapsedRealtime()}. 341 * <p> 342 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 343 * exception. 344 */ getRangingTimestampMillis()345 public long getRangingTimestampMillis() { 346 if (mStatus != STATUS_SUCCESS) { 347 throw new IllegalStateException( 348 "getRangingTimestampMillis(): invoked on an invalid result: getStatus()=" 349 + mStatus); 350 } 351 return mTimestamp; 352 } 353 354 /** 355 * @return The result is true if the IEEE 802.11mc protocol was used (also known as 356 * two-sided RTT). If the result is false, a one-side RTT result is provided which does not 357 * subtract the turnaround time at the responder. 358 * <p> 359 * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an 360 * exception. 361 */ is80211mcMeasurement()362 public boolean is80211mcMeasurement() { 363 if (mStatus != STATUS_SUCCESS) { 364 throw new IllegalStateException( 365 "is80211mcMeasurementResult(): invoked on an invalid result: getStatus()=" 366 + mStatus); 367 } 368 return mIs80211mcMeasurement; 369 } 370 371 @Override describeContents()372 public int describeContents() { 373 return 0; 374 } 375 376 @Override writeToParcel(Parcel dest, int flags)377 public void writeToParcel(Parcel dest, int flags) { 378 dest.writeInt(mStatus); 379 if (mMac == null) { 380 dest.writeBoolean(false); 381 } else { 382 dest.writeBoolean(true); 383 mMac.writeToParcel(dest, flags); 384 } 385 if (mPeerHandle == null) { 386 dest.writeBoolean(false); 387 } else { 388 dest.writeBoolean(true); 389 dest.writeInt(mPeerHandle.peerId); 390 } 391 dest.writeInt(mDistanceMm); 392 dest.writeInt(mDistanceStdDevMm); 393 dest.writeInt(mRssi); 394 dest.writeInt(mNumAttemptedMeasurements); 395 dest.writeInt(mNumSuccessfulMeasurements); 396 dest.writeByteArray(mLci); 397 dest.writeByteArray(mLcr); 398 dest.writeParcelable(mResponderLocation, flags); 399 dest.writeLong(mTimestamp); 400 dest.writeBoolean(mIs80211mcMeasurement); 401 } 402 403 public static final @android.annotation.NonNull Creator<RangingResult> CREATOR = new Creator<RangingResult>() { 404 @Override 405 public RangingResult[] newArray(int size) { 406 return new RangingResult[size]; 407 } 408 409 @Override 410 public RangingResult createFromParcel(Parcel in) { 411 int status = in.readInt(); 412 boolean macAddressPresent = in.readBoolean(); 413 MacAddress mac = null; 414 if (macAddressPresent) { 415 mac = MacAddress.CREATOR.createFromParcel(in); 416 } 417 boolean peerHandlePresent = in.readBoolean(); 418 PeerHandle peerHandle = null; 419 if (peerHandlePresent) { 420 peerHandle = new PeerHandle(in.readInt()); 421 } 422 int distanceMm = in.readInt(); 423 int distanceStdDevMm = in.readInt(); 424 int rssi = in.readInt(); 425 int numAttemptedMeasurements = in.readInt(); 426 int numSuccessfulMeasurements = in.readInt(); 427 byte[] lci = in.createByteArray(); 428 byte[] lcr = in.createByteArray(); 429 ResponderLocation responderLocation = 430 in.readParcelable(this.getClass().getClassLoader()); 431 long timestamp = in.readLong(); 432 boolean isllmcMeasurement = in.readBoolean(); 433 if (peerHandlePresent) { 434 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi, 435 numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, 436 responderLocation, timestamp); 437 } else { 438 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi, 439 numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, 440 responderLocation, timestamp, isllmcMeasurement); 441 } 442 } 443 }; 444 445 /** @hide */ 446 @Override toString()447 public String toString() { 448 return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append( 449 mMac).append(", peerHandle=").append( 450 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append( 451 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append( 452 ", rssi=").append(mRssi).append(", numAttemptedMeasurements=").append( 453 mNumAttemptedMeasurements).append(", numSuccessfulMeasurements=").append( 454 mNumSuccessfulMeasurements).append(", lci=").append(mLci).append(", lcr=").append( 455 mLcr).append(", responderLocation=").append(mResponderLocation) 456 .append(", timestamp=").append(mTimestamp).append(", is80211mcMeasurement=") 457 .append(mIs80211mcMeasurement).append("]").toString(); 458 } 459 460 @Override equals(Object o)461 public boolean equals(Object o) { 462 if (this == o) { 463 return true; 464 } 465 466 if (!(o instanceof RangingResult)) { 467 return false; 468 } 469 470 RangingResult lhs = (RangingResult) o; 471 472 return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals( 473 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm 474 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi 475 && mNumAttemptedMeasurements == lhs.mNumAttemptedMeasurements 476 && mNumSuccessfulMeasurements == lhs.mNumSuccessfulMeasurements 477 && Arrays.equals(mLci, lhs.mLci) && Arrays.equals(mLcr, lhs.mLcr) 478 && mTimestamp == lhs.mTimestamp 479 && mIs80211mcMeasurement == lhs.mIs80211mcMeasurement 480 && Objects.equals(mResponderLocation, lhs.mResponderLocation); 481 } 482 483 @Override hashCode()484 public int hashCode() { 485 return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi, 486 mNumAttemptedMeasurements, mNumSuccessfulMeasurements, mLci, mLcr, 487 mResponderLocation, mTimestamp, mIs80211mcMeasurement); 488 } 489 } 490