1 /* 2 * Copyright 2020 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.uwb; 18 19 import android.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.annotation.SystemApi; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.os.PersistableBundle; 28 import android.os.SystemClock; 29 import android.uwb.util.PersistableBundleUtils; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.Objects; 34 35 /** 36 * Representation of a ranging measurement between the local device and a remote device 37 * 38 * @hide 39 */ 40 @SystemApi 41 public final class RangingMeasurement implements Parcelable { 42 public static final int RSSI_UNKNOWN = -128; 43 public static final int RSSI_MIN = -127; 44 public static final int RSSI_MAX = -1; 45 46 private final UwbAddress mRemoteDeviceAddress; 47 private final @Status int mStatus; 48 private final long mElapsedRealtimeNanos; 49 private final DistanceMeasurement mDistanceMeasurement; 50 private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement; 51 private final AngleOfArrivalMeasurement mDestinationAngleOfArrivalMeasurement; 52 private final @LineOfSight int mLineOfSight; 53 private final @MeasurementFocus int mMeasurementFocus; 54 private final int mRssiDbm; 55 private final PersistableBundle mRangingMeasurementMetadata; 56 RangingMeasurement(@onNull UwbAddress remoteDeviceAddress, @Status int status, long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement, @Nullable AngleOfArrivalMeasurement destinationAngleOfArrivalMeasurement, @LineOfSight int lineOfSight, @MeasurementFocus int measurementFocus, @IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int rssiDbm, PersistableBundle rangingMeasurementMetadata)57 private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status, 58 long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, 59 @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement, 60 @Nullable AngleOfArrivalMeasurement destinationAngleOfArrivalMeasurement, 61 @LineOfSight int lineOfSight, @MeasurementFocus int measurementFocus, 62 @IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int rssiDbm, 63 PersistableBundle rangingMeasurementMetadata) { 64 mRemoteDeviceAddress = remoteDeviceAddress; 65 mStatus = status; 66 mElapsedRealtimeNanos = elapsedRealtimeNanos; 67 mDistanceMeasurement = distanceMeasurement; 68 mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; 69 mDestinationAngleOfArrivalMeasurement = destinationAngleOfArrivalMeasurement; 70 mLineOfSight = lineOfSight; 71 mMeasurementFocus = measurementFocus; 72 mRssiDbm = rssiDbm; 73 mRangingMeasurementMetadata = rangingMeasurementMetadata; 74 } 75 76 /** 77 * Get the remote device's {@link UwbAddress} 78 * 79 * @return the remote device's {@link UwbAddress} 80 */ 81 @NonNull getRemoteDeviceAddress()82 public UwbAddress getRemoteDeviceAddress() { 83 return mRemoteDeviceAddress; 84 } 85 86 /** 87 * @hide 88 */ 89 @Retention(RetentionPolicy.SOURCE) 90 @IntDef(value = { 91 RANGING_STATUS_SUCCESS, 92 RANGING_STATUS_FAILURE_OUT_OF_RANGE, 93 RANGING_STATUS_FAILURE_UNKNOWN_ERROR}) 94 public @interface Status {} 95 96 /** 97 * Ranging attempt was successful for this device 98 */ 99 public static final int RANGING_STATUS_SUCCESS = 0; 100 101 /** 102 * Ranging failed for this device because it is out of range 103 */ 104 public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; 105 106 /** 107 * Ranging failed for this device because of unknown error 108 */ 109 public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; 110 111 /** 112 * Get the status of this ranging measurement 113 * 114 * <p>Possible values are 115 * {@link #RANGING_STATUS_SUCCESS}, 116 * {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE}, 117 * {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}. 118 * 119 * @return the status of the ranging measurement 120 */ 121 @Status getStatus()122 public int getStatus() { 123 return mStatus; 124 } 125 126 /** 127 * Timestamp of this ranging measurement in time since boot nanos in the same namespace as 128 * {@link SystemClock#elapsedRealtimeNanos()} 129 * 130 * @return timestamp of ranging measurement in nanoseconds 131 */ 132 @SuppressLint("MethodNameUnits") getElapsedRealtimeNanos()133 public long getElapsedRealtimeNanos() { 134 return mElapsedRealtimeNanos; 135 } 136 137 /** 138 * Get the distance measurement 139 * 140 * @return a {@link DistanceMeasurement} or null if {@link #getStatus()} != 141 * {@link #RANGING_STATUS_SUCCESS} 142 */ 143 @Nullable getDistanceMeasurement()144 public DistanceMeasurement getDistanceMeasurement() { 145 return mDistanceMeasurement; 146 } 147 148 /** 149 * Get the angle of arrival measurement 150 * 151 * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} != 152 * {@link #RANGING_STATUS_SUCCESS} 153 */ 154 @Nullable getAngleOfArrivalMeasurement()155 public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() { 156 return mAngleOfArrivalMeasurement; 157 } 158 159 /** 160 * Get the angle of arrival measurement at the destination. 161 * 162 * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} != 163 * {@link #RANGING_STATUS_SUCCESS} 164 */ 165 @Nullable getDestinationAngleOfArrivalMeasurement()166 public AngleOfArrivalMeasurement getDestinationAngleOfArrivalMeasurement() { 167 return mDestinationAngleOfArrivalMeasurement; 168 } 169 170 /** 171 * @hide 172 */ 173 @Retention(RetentionPolicy.SOURCE) 174 @IntDef(value = { 175 LOS, 176 NLOS, 177 LOS_UNDETERMINED}) 178 public @interface LineOfSight {} 179 180 /** 181 * If measurement was in line of sight. 182 */ 183 public static final int LOS = 0; 184 185 /** 186 * If measurement was not in line of sight. 187 */ 188 public static final int NLOS = 1; 189 190 /** 191 * Unable to determine whether the measurement was in line of sight or not. 192 */ 193 public static final int LOS_UNDETERMINED = 0xFF; 194 195 /** 196 * Get whether the measurement was in Line of sight or non-line of sight. 197 * 198 * @return whether the measurement was in line of sight or not 199 */ getLineOfSight()200 public @LineOfSight int getLineOfSight() { 201 return mLineOfSight; 202 } 203 204 /** 205 * Get the measured RSSI in dBm 206 */ getRssiDbm()207 public @IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int getRssiDbm() { 208 return mRssiDbm; 209 } 210 211 /** 212 * @hide 213 */ 214 @Retention(RetentionPolicy.SOURCE) 215 @IntDef(value = { 216 MEASUREMENT_FOCUS_NONE, 217 MEASUREMENT_FOCUS_RANGE, 218 MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_AZIMUTH, 219 MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_ELEVATION}) 220 public @interface MeasurementFocus {} 221 222 /** 223 * Ranging measurement was done with no particular focus in terms of antenna selection. 224 */ 225 public static final int MEASUREMENT_FOCUS_NONE = 0; 226 227 /** 228 * Ranging measurement was done with a focus on range calculation in terms of antenna 229 * selection. 230 */ 231 public static final int MEASUREMENT_FOCUS_RANGE = 1; 232 233 /** 234 * Ranging measurement was done with a focus on Angle of arrival azimuth calculation in terms of 235 * antenna selection. 236 */ 237 public static final int MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_AZIMUTH = 2; 238 239 /** 240 * Ranging measurement was done with a focus on Angle of arrival elevation calculation in terms 241 * of antenna selection. 242 */ 243 public static final int MEASUREMENT_FOCUS_ANGLE_OF_ARRIVAL_ELEVATION = 3; 244 245 /** 246 * Gets the measurement focus in terms of antenna used for this measurement. 247 * 248 * @return focus of this measurement. 249 */ getMeasurementFocus()250 public @MeasurementFocus int getMeasurementFocus() { 251 return mMeasurementFocus; 252 } 253 254 /** 255 * Gets ranging measurement metadata passed by vendor 256 * 257 * @return vendor data for ranging measurement 258 */ 259 @NonNull getRangingMeasurementMetadata()260 public PersistableBundle getRangingMeasurementMetadata() { 261 return mRangingMeasurementMetadata; 262 } 263 264 /** 265 * @hide 266 */ 267 @Override equals(@ullable Object obj)268 public boolean equals(@Nullable Object obj) { 269 if (this == obj) { 270 return true; 271 } 272 273 if (obj instanceof RangingMeasurement) { 274 RangingMeasurement other = (RangingMeasurement) obj; 275 return Objects.equals(mRemoteDeviceAddress, other.getRemoteDeviceAddress()) 276 && mStatus == other.getStatus() 277 && mElapsedRealtimeNanos == other.getElapsedRealtimeNanos() 278 && Objects.equals(mDistanceMeasurement, other.getDistanceMeasurement()) 279 && Objects.equals( 280 mAngleOfArrivalMeasurement, other.getAngleOfArrivalMeasurement()) 281 && Objects.equals( 282 mDestinationAngleOfArrivalMeasurement, 283 other.getDestinationAngleOfArrivalMeasurement()) 284 && mLineOfSight == other.getLineOfSight() 285 && mMeasurementFocus == other.getMeasurementFocus() 286 && mRssiDbm == other.getRssiDbm() 287 && PersistableBundleUtils.isEqual(mRangingMeasurementMetadata, 288 other.mRangingMeasurementMetadata); 289 } 290 return false; 291 } 292 293 /** 294 * @hide 295 */ 296 @Override hashCode()297 public int hashCode() { 298 return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, 299 mDistanceMeasurement, mAngleOfArrivalMeasurement, 300 mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, mRssiDbm, 301 PersistableBundleUtils.getHashCode(mRangingMeasurementMetadata)); 302 } 303 304 @Override describeContents()305 public int describeContents() { 306 return 0; 307 } 308 309 @Override writeToParcel(@onNull Parcel dest, int flags)310 public void writeToParcel(@NonNull Parcel dest, int flags) { 311 dest.writeParcelable(mRemoteDeviceAddress, flags); 312 dest.writeInt(mStatus); 313 dest.writeLong(mElapsedRealtimeNanos); 314 dest.writeParcelable(mDistanceMeasurement, flags); 315 dest.writeParcelable(mAngleOfArrivalMeasurement, flags); 316 dest.writeParcelable(mDestinationAngleOfArrivalMeasurement, flags); 317 dest.writeInt(mLineOfSight); 318 dest.writeInt(mMeasurementFocus); 319 dest.writeInt(mRssiDbm); 320 dest.writePersistableBundle(mRangingMeasurementMetadata); 321 } 322 323 public static final @android.annotation.NonNull Creator<RangingMeasurement> CREATOR = 324 new Creator<RangingMeasurement>() { 325 @Override 326 public RangingMeasurement createFromParcel(Parcel in) { 327 Builder builder = new Builder(); 328 builder.setRemoteDeviceAddress( 329 in.readParcelable(UwbAddress.class.getClassLoader())); 330 builder.setStatus(in.readInt()); 331 builder.setElapsedRealtimeNanos(in.readLong()); 332 builder.setDistanceMeasurement( 333 in.readParcelable(DistanceMeasurement.class.getClassLoader())); 334 builder.setAngleOfArrivalMeasurement( 335 in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader())); 336 builder.setDestinationAngleOfArrivalMeasurement( 337 in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader())); 338 builder.setLineOfSight(in.readInt()); 339 builder.setMeasurementFocus(in.readInt()); 340 builder.setRssiDbm(in.readInt()); 341 PersistableBundle metadata = 342 in.readPersistableBundle(getClass().getClassLoader()); 343 if (metadata != null) builder.setRangingMeasurementMetadata(metadata); 344 return builder.build(); 345 } 346 347 @Override 348 public RangingMeasurement[] newArray(int size) { 349 return new RangingMeasurement[size]; 350 } 351 }; 352 353 /** @hide **/ 354 @Override toString()355 public String toString() { 356 return "RangingMeasurement[" 357 + "remote device address:" + mRemoteDeviceAddress 358 + ", distance measurement: " + mDistanceMeasurement 359 + ", aoa measurement: " + mAngleOfArrivalMeasurement 360 + ", dest aoa measurement: " + mDestinationAngleOfArrivalMeasurement 361 + ", lineOfSight: " + mLineOfSight 362 + ", measurementFocus: " + mMeasurementFocus 363 + ", rssiDbm: " + mRssiDbm 364 + ", ranging measurement metadata: " + mRangingMeasurementMetadata 365 + ", elapsed real time nanos: " + mElapsedRealtimeNanos 366 + ", status: " + mStatus 367 + "]"; 368 } 369 370 /** 371 * Builder for a {@link RangingMeasurement} object. 372 */ 373 public static final class Builder { 374 private UwbAddress mRemoteDeviceAddress = null; 375 private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR; 376 private long mElapsedRealtimeNanos = -1L; 377 private DistanceMeasurement mDistanceMeasurement = null; 378 private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null; 379 private AngleOfArrivalMeasurement mDestinationAngleOfArrivalMeasurement = null; 380 private @LineOfSight int mLineOfSight = LOS_UNDETERMINED; 381 private @MeasurementFocus int mMeasurementFocus = MEASUREMENT_FOCUS_NONE; 382 private int mRssiDbm = RSSI_UNKNOWN; 383 private PersistableBundle mRangingMeasurementMetadata = null; 384 385 /** 386 * Set the remote device address that this measurement is for 387 * 388 * @param remoteDeviceAddress remote device's address 389 */ 390 @NonNull setRemoteDeviceAddress(@onNull UwbAddress remoteDeviceAddress)391 public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) { 392 mRemoteDeviceAddress = remoteDeviceAddress; 393 return this; 394 } 395 396 /** 397 * Set the status of ranging measurement 398 * 399 * @param status the status of the ranging measurement 400 */ 401 @NonNull setStatus(@tatus int status)402 public Builder setStatus(@Status int status) { 403 mStatus = status; 404 return this; 405 } 406 407 /** 408 * Set the elapsed realtime in nanoseconds when the ranging measurement occurred 409 * 410 * @param elapsedRealtimeNanos time the ranging measurement occurred 411 */ 412 @NonNull setElapsedRealtimeNanos(long elapsedRealtimeNanos)413 public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) { 414 if (elapsedRealtimeNanos < 0) { 415 throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0"); 416 } 417 mElapsedRealtimeNanos = elapsedRealtimeNanos; 418 return this; 419 } 420 421 /** 422 * Set the {@link DistanceMeasurement} 423 * 424 * @param distanceMeasurement the distance measurement for this ranging measurement 425 */ 426 @NonNull setDistanceMeasurement(@onNull DistanceMeasurement distanceMeasurement)427 public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) { 428 mDistanceMeasurement = distanceMeasurement; 429 return this; 430 } 431 432 /** 433 * Set the {@link AngleOfArrivalMeasurement} 434 * 435 * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging 436 * measurement 437 */ 438 @NonNull setAngleOfArrivalMeasurement( @onNull AngleOfArrivalMeasurement angleOfArrivalMeasurement)439 public Builder setAngleOfArrivalMeasurement( 440 @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) { 441 mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; 442 return this; 443 } 444 445 /** 446 * Set the {@link AngleOfArrivalMeasurement} at the destination. 447 * 448 * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging 449 * measurement 450 */ 451 @NonNull setDestinationAngleOfArrivalMeasurement( @onNull AngleOfArrivalMeasurement angleOfArrivalMeasurement)452 public Builder setDestinationAngleOfArrivalMeasurement( 453 @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) { 454 mDestinationAngleOfArrivalMeasurement = angleOfArrivalMeasurement; 455 return this; 456 } 457 458 /** 459 * Set whether the measurement was in Line of sight or non-line of sight. 460 * 461 * @param lineOfSight whether the measurement was in line of sight or not 462 */ 463 @NonNull setLineOfSight(@ineOfSight int lineOfSight)464 public Builder setLineOfSight(@LineOfSight int lineOfSight) { 465 mLineOfSight = lineOfSight; 466 return this; 467 } 468 469 /** 470 * Sets the measurement focus in terms of antenna used for this measurement. 471 * 472 * @param measurementFocus focus of this measurement. 473 */ 474 @NonNull setMeasurementFocus(@easurementFocus int measurementFocus)475 public Builder setMeasurementFocus(@MeasurementFocus int measurementFocus) { 476 mMeasurementFocus = measurementFocus; 477 return this; 478 } 479 480 /** 481 * Set the RSSI in dBm 482 * 483 * @param rssiDbm the measured RSSI in dBm 484 */ 485 @NonNull setRssiDbm(@ntRangefrom = RSSI_UNKNOWN, to = RSSI_MAX) int rssiDbm)486 public Builder setRssiDbm(@IntRange(from = RSSI_UNKNOWN, to = RSSI_MAX) int rssiDbm) { 487 if (rssiDbm != RSSI_UNKNOWN && (rssiDbm < RSSI_MIN || rssiDbm > RSSI_MAX)) { 488 throw new IllegalArgumentException("Invalid rssiDbm: " + rssiDbm); 489 } 490 mRssiDbm = rssiDbm; 491 return this; 492 } 493 494 /** 495 * Set Ranging measurement metadata 496 * 497 * @param rangingMeasurementMetadata vendor data per ranging measurement 498 * 499 * @throws IllegalStateException if rangingMeasurementMetadata is null 500 */ 501 @NonNull setRangingMeasurementMetadata(@onNull PersistableBundle rangingMeasurementMetadata)502 public Builder setRangingMeasurementMetadata(@NonNull 503 PersistableBundle rangingMeasurementMetadata) { 504 if (rangingMeasurementMetadata == null) { 505 throw new IllegalStateException("Expected non-null rangingMeasurementMetadata"); 506 } 507 mRangingMeasurementMetadata = rangingMeasurementMetadata; 508 return this; 509 } 510 511 /** 512 * Build the {@link RangingMeasurement} object 513 * 514 * @throws IllegalStateException if a distance or angle of arrival measurement is provided 515 * but the measurement was not successful, if the 516 * elapsedRealtimeNanos of the measurement is invalid, or 517 * if no remote device address is set 518 */ 519 @NonNull build()520 public RangingMeasurement build() { 521 if (mStatus != RANGING_STATUS_SUCCESS) { 522 if (mDistanceMeasurement != null) { 523 throw new IllegalStateException( 524 "Distance Measurement must be null if ranging is not successful"); 525 } 526 527 if (mAngleOfArrivalMeasurement != null) { 528 throw new IllegalStateException( 529 "Angle of Arrival must be null if ranging is not successful"); 530 } 531 532 // Destination AOA is optional according to the spec. 533 } 534 535 if (mRemoteDeviceAddress == null) { 536 throw new IllegalStateException("No remote device address was set"); 537 } 538 539 if (mElapsedRealtimeNanos < 0) { 540 throw new IllegalStateException( 541 "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos); 542 } 543 544 return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, 545 mDistanceMeasurement, mAngleOfArrivalMeasurement, 546 mDestinationAngleOfArrivalMeasurement, mLineOfSight, mMeasurementFocus, 547 mRssiDbm, mRangingMeasurementMetadata); 548 } 549 } 550 } 551