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