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.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SuppressLint; 23 import android.annotation.SystemApi; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.os.SystemClock; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Objects; 31 32 /** 33 * Representation of a ranging measurement between the local device and a remote device 34 * 35 * @hide 36 */ 37 @SystemApi 38 public final class RangingMeasurement implements Parcelable { 39 private final UwbAddress mRemoteDeviceAddress; 40 private final @Status int mStatus; 41 private final long mElapsedRealtimeNanos; 42 private final DistanceMeasurement mDistanceMeasurement; 43 private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement; 44 RangingMeasurement(@onNull UwbAddress remoteDeviceAddress, @Status int status, long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement)45 private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status, 46 long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, 47 @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) { 48 mRemoteDeviceAddress = remoteDeviceAddress; 49 mStatus = status; 50 mElapsedRealtimeNanos = elapsedRealtimeNanos; 51 mDistanceMeasurement = distanceMeasurement; 52 mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; 53 } 54 55 /** 56 * Get the remote device's {@link UwbAddress} 57 * 58 * @return the remote device's {@link UwbAddress} 59 */ 60 @NonNull getRemoteDeviceAddress()61 public UwbAddress getRemoteDeviceAddress() { 62 return mRemoteDeviceAddress; 63 } 64 65 /** 66 * @hide 67 */ 68 @Retention(RetentionPolicy.SOURCE) 69 @IntDef(value = { 70 RANGING_STATUS_SUCCESS, 71 RANGING_STATUS_FAILURE_OUT_OF_RANGE, 72 RANGING_STATUS_FAILURE_UNKNOWN_ERROR}) 73 public @interface Status {} 74 75 /** 76 * Ranging attempt was successful for this device 77 */ 78 public static final int RANGING_STATUS_SUCCESS = 0; 79 80 /** 81 * Ranging failed for this device because it is out of range 82 */ 83 public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; 84 85 /** 86 * Ranging failed for this device because of unknown error 87 */ 88 public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; 89 90 /** 91 * Get the status of this ranging measurement 92 * 93 * <p>Possible values are 94 * {@link #RANGING_STATUS_SUCCESS}, 95 * {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE}, 96 * {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}. 97 * 98 * @return the status of the ranging measurement 99 */ 100 @Status getStatus()101 public int getStatus() { 102 return mStatus; 103 } 104 105 /** 106 * Timestamp of this ranging measurement in time since boot nanos in the same namespace as 107 * {@link SystemClock#elapsedRealtimeNanos()} 108 * 109 * @return timestamp of ranging measurement in nanoseconds 110 */ 111 @SuppressLint("MethodNameUnits") getElapsedRealtimeNanos()112 public long getElapsedRealtimeNanos() { 113 return mElapsedRealtimeNanos; 114 } 115 116 /** 117 * Get the distance measurement 118 * 119 * @return a {@link DistanceMeasurement} or null if {@link #getStatus()} != 120 * {@link #RANGING_STATUS_SUCCESS} 121 */ 122 @Nullable getDistanceMeasurement()123 public DistanceMeasurement getDistanceMeasurement() { 124 return mDistanceMeasurement; 125 } 126 127 /** 128 * Get the angle of arrival measurement 129 * 130 * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} != 131 * {@link #RANGING_STATUS_SUCCESS} 132 */ 133 @Nullable getAngleOfArrivalMeasurement()134 public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() { 135 return mAngleOfArrivalMeasurement; 136 } 137 138 /** 139 * @hide 140 */ 141 @Override equals(@ullable Object obj)142 public boolean equals(@Nullable Object obj) { 143 if (this == obj) { 144 return true; 145 } 146 147 if (obj instanceof RangingMeasurement) { 148 RangingMeasurement other = (RangingMeasurement) obj; 149 return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress()) 150 && mStatus == other.getStatus() 151 && mElapsedRealtimeNanos == other.getElapsedRealtimeNanos() 152 && mDistanceMeasurement.equals(other.getDistanceMeasurement()) 153 && mAngleOfArrivalMeasurement.equals(other.getAngleOfArrivalMeasurement()); 154 } 155 return false; 156 } 157 158 /** 159 * @hide 160 */ 161 @Override hashCode()162 public int hashCode() { 163 return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, 164 mDistanceMeasurement, mAngleOfArrivalMeasurement); 165 } 166 167 @Override describeContents()168 public int describeContents() { 169 return 0; 170 } 171 172 @Override writeToParcel(@onNull Parcel dest, int flags)173 public void writeToParcel(@NonNull Parcel dest, int flags) { 174 dest.writeParcelable(mRemoteDeviceAddress, flags); 175 dest.writeInt(mStatus); 176 dest.writeLong(mElapsedRealtimeNanos); 177 dest.writeParcelable(mDistanceMeasurement, flags); 178 dest.writeParcelable(mAngleOfArrivalMeasurement, flags); 179 } 180 181 public static final @android.annotation.NonNull Creator<RangingMeasurement> CREATOR = 182 new Creator<RangingMeasurement>() { 183 @Override 184 public RangingMeasurement createFromParcel(Parcel in) { 185 Builder builder = new Builder(); 186 builder.setRemoteDeviceAddress( 187 in.readParcelable(UwbAddress.class.getClassLoader())); 188 builder.setStatus(in.readInt()); 189 builder.setElapsedRealtimeNanos(in.readLong()); 190 builder.setDistanceMeasurement( 191 in.readParcelable(DistanceMeasurement.class.getClassLoader())); 192 builder.setAngleOfArrivalMeasurement( 193 in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader())); 194 return builder.build(); 195 } 196 197 @Override 198 public RangingMeasurement[] newArray(int size) { 199 return new RangingMeasurement[size]; 200 } 201 }; 202 203 /** 204 * Builder for a {@link RangingMeasurement} object. 205 */ 206 public static final class Builder { 207 private UwbAddress mRemoteDeviceAddress = null; 208 private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR; 209 private long mElapsedRealtimeNanos = -1L; 210 private DistanceMeasurement mDistanceMeasurement = null; 211 private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null; 212 213 /** 214 * Set the remote device address that this measurement is for 215 * 216 * @param remoteDeviceAddress remote device's address 217 */ 218 @NonNull setRemoteDeviceAddress(@onNull UwbAddress remoteDeviceAddress)219 public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) { 220 mRemoteDeviceAddress = remoteDeviceAddress; 221 return this; 222 } 223 224 /** 225 * Set the status of ranging measurement 226 * 227 * @param status the status of the ranging measurement 228 */ 229 @NonNull setStatus(@tatus int status)230 public Builder setStatus(@Status int status) { 231 mStatus = status; 232 return this; 233 } 234 235 /** 236 * Set the elapsed realtime in nanoseconds when the ranging measurement occurred 237 * 238 * @param elapsedRealtimeNanos time the ranging measurement occurred 239 */ 240 @NonNull setElapsedRealtimeNanos(long elapsedRealtimeNanos)241 public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) { 242 if (elapsedRealtimeNanos < 0) { 243 throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0"); 244 } 245 mElapsedRealtimeNanos = elapsedRealtimeNanos; 246 return this; 247 } 248 249 /** 250 * Set the {@link DistanceMeasurement} 251 * 252 * @param distanceMeasurement the distance measurement for this ranging measurement 253 */ 254 @NonNull setDistanceMeasurement(@onNull DistanceMeasurement distanceMeasurement)255 public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) { 256 mDistanceMeasurement = distanceMeasurement; 257 return this; 258 } 259 260 /** 261 * Set the {@link AngleOfArrivalMeasurement} 262 * 263 * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging 264 * measurement 265 */ 266 @NonNull setAngleOfArrivalMeasurement( @onNull AngleOfArrivalMeasurement angleOfArrivalMeasurement)267 public Builder setAngleOfArrivalMeasurement( 268 @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) { 269 mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; 270 return this; 271 } 272 273 /** 274 * Build the {@link RangingMeasurement} object 275 * 276 * @throws IllegalStateException if a distance or angle of arrival measurement is provided 277 * but the measurement was not successful, if the 278 * elapsedRealtimeNanos of the measurement is invalid, or 279 * if no remote device address is set 280 */ 281 @NonNull build()282 public RangingMeasurement build() { 283 if (mStatus != RANGING_STATUS_SUCCESS) { 284 if (mDistanceMeasurement != null) { 285 throw new IllegalStateException( 286 "Distance Measurement must be null if ranging is not successful"); 287 } 288 289 if (mAngleOfArrivalMeasurement != null) { 290 throw new IllegalStateException( 291 "Angle of Arrival must be null if ranging is not successful"); 292 } 293 } 294 295 if (mRemoteDeviceAddress == null) { 296 throw new IllegalStateException("No remote device address was set"); 297 } 298 299 if (mElapsedRealtimeNanos < 0) { 300 throw new IllegalStateException( 301 "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos); 302 } 303 304 return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, 305 mDistanceMeasurement, mAngleOfArrivalMeasurement); 306 } 307 } 308 } 309