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.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.PersistableBundle;
25 import android.uwb.util.PersistableBundleUtils;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Objects;
30 
31 /**
32  * This class contains the UWB ranging data
33  *
34  * @hide
35  */
36 @SystemApi
37 public final class RangingReport implements Parcelable {
38     private final List<RangingMeasurement> mRangingMeasurements;
39     private final PersistableBundle mRangingReportMetadata;
40 
RangingReport(@onNull List<RangingMeasurement> rangingMeasurements, PersistableBundle rangingReportMetadata)41     private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements,
42             PersistableBundle rangingReportMetadata) {
43         mRangingMeasurements = rangingMeasurements;
44         mRangingReportMetadata = rangingReportMetadata;
45     }
46 
47     /**
48      * Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval
49      * <p>The underlying UWB adapter may choose to do multiple measurements in each ranging
50      * interval.
51      *
52      * <p>The entries in the {@link List} are ordered in ascending order based on
53      * {@link RangingMeasurement#getElapsedRealtimeNanos()}
54      *
55      * @return a {@link List} of {@link RangingMeasurement} objects
56      */
57     @NonNull
getMeasurements()58     public List<RangingMeasurement> getMeasurements() {
59         return mRangingMeasurements;
60     }
61 
62     /**
63      * Gets ranging report metadata passed by vendor
64      *
65      * @return vendor data for ranging report
66      */
67     @NonNull
getRangingReportMetadata()68     public PersistableBundle getRangingReportMetadata() {
69         return mRangingReportMetadata;
70     }
71 
72     /**
73      * @hide
74      */
75     @Override
equals(@ullable Object obj)76     public boolean equals(@Nullable Object obj) {
77         if (this == obj) {
78             return true;
79         }
80 
81         if (obj instanceof RangingReport) {
82             RangingReport other = (RangingReport) obj;
83             return mRangingMeasurements.equals(other.getMeasurements())
84                     && PersistableBundleUtils.isEqual(mRangingReportMetadata,
85                     other.getRangingReportMetadata());
86         }
87         return false;
88     }
89 
90     /**
91      * @hide
92      */
93     @Override
hashCode()94     public int hashCode() {
95         return Objects.hash(mRangingMeasurements, PersistableBundleUtils
96                 .getHashCode(mRangingReportMetadata));
97     }
98 
99     @Override
describeContents()100     public int describeContents() {
101         return 0;
102     }
103 
104     @Override
writeToParcel(@onNull Parcel dest, int flags)105     public void writeToParcel(@NonNull Parcel dest, int flags) {
106         dest.writeTypedList(mRangingMeasurements);
107         dest.writePersistableBundle(mRangingReportMetadata);
108     }
109 
110     public static final @android.annotation.NonNull Creator<RangingReport> CREATOR =
111             new Creator<RangingReport>() {
112                 @Override
113                 public RangingReport createFromParcel(Parcel in) {
114                     Builder builder = new Builder();
115                     builder.addMeasurements(in.createTypedArrayList(RangingMeasurement.CREATOR));
116                     PersistableBundle metadata =
117                             in.readPersistableBundle(getClass().getClassLoader());
118                     if (metadata != null) builder.addRangingReportMetadata(metadata);
119                     return builder.build();
120                 }
121 
122                 @Override
123                 public RangingReport[] newArray(int size) {
124                     return new RangingReport[size];
125                 }
126     };
127 
128     /** @hide **/
129     @Override
toString()130     public String toString() {
131         return "RangingReport["
132                 + "measurements: " + mRangingMeasurements
133                 + ", ranging report measurement: " + mRangingReportMetadata
134                 + "]";
135     }
136 
137     /**
138      * Builder for {@link RangingReport} object
139      */
140     public static final class Builder {
141         List<RangingMeasurement> mMeasurements = new ArrayList<>();
142         private PersistableBundle mRangingReportMetadata = new PersistableBundle();
143 
144         /**
145          * Add a single {@link RangingMeasurement}
146          *
147          * @param rangingMeasurement a ranging measurement
148          */
149         @NonNull
addMeasurement(@onNull RangingMeasurement rangingMeasurement)150         public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
151             mMeasurements.add(rangingMeasurement);
152             return this;
153         }
154 
155         /**
156          * Add a {@link List} of {@link RangingMeasurement}s
157          *
158          * @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
159          */
160         @NonNull
addMeasurements(@onNull List<RangingMeasurement> rangingMeasurements)161         public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
162             mMeasurements.addAll(rangingMeasurements);
163             return this;
164         }
165 
166         /**
167          * Add ranging report metadata
168          *
169          * @param rangingReportMetadata vendor data per ranging report
170          *
171          * @throws IllegalStateException if rangingReportMetadata is null
172          */
173         @NonNull
addRangingReportMetadata(@onNull PersistableBundle rangingReportMetadata)174         public Builder addRangingReportMetadata(@NonNull PersistableBundle rangingReportMetadata) {
175             if (rangingReportMetadata == null) {
176                 throw new IllegalStateException("Expected non-null rangingReportMetadata");
177             }
178             mRangingReportMetadata = rangingReportMetadata;
179             return this;
180         }
181 
182         /**
183          * Build the {@link RangingReport} object
184          *
185          * @throws IllegalStateException if measurements are not in monotonically increasing order
186          */
187         @NonNull
build()188         public RangingReport build() {
189             // Verify that all measurement timestamps are monotonically increasing
190             RangingMeasurement prevMeasurement = null;
191             for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) {
192                 RangingMeasurement curMeasurement = mMeasurements.get(curIndex);
193                 if (prevMeasurement != null
194                         && (prevMeasurement.getElapsedRealtimeNanos()
195                                 > curMeasurement.getElapsedRealtimeNanos())) {
196                     throw new IllegalStateException(
197                             "Timestamp (" + curMeasurement.getElapsedRealtimeNanos()
198                             + ") at index " + curIndex + " is less than previous timestamp ("
199                             + prevMeasurement.getElapsedRealtimeNanos() + ")");
200                 }
201                 prevMeasurement = curMeasurement;
202             }
203             return new RangingReport(mMeasurements, mRangingReportMetadata);
204         }
205     }
206 }
207 
208