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