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