• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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