1 /* 2 * Copyright (C) 2022 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.bluetooth.le; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.FloatRange; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.SystemApi; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 27 import com.android.bluetooth.flags.Flags; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 32 /** 33 * Result of distance measurement. 34 * 35 * @hide 36 */ 37 @SystemApi 38 public final class DistanceMeasurementResult implements Parcelable { 39 40 /** 41 * Normalized Attack Detector Metric. See Channel Sounding CR_PR, 3.13.24 for details. 42 * 43 * <p>Specification: https://www.bluetooth.com/specifications/specs/channel-sounding-cr-pr/ 44 * 45 * @hide 46 */ 47 @Retention(RetentionPolicy.SOURCE) 48 @IntDef( 49 value = { 50 NADM_ATTACK_IS_EXTREMELY_UNLIKELY, 51 NADM_ATTACK_IS_VERY_UNLIKELY, 52 NADM_ATTACK_IS_UNLIKELY, 53 NADM_ATTACK_IS_POSSIBLE, 54 NADM_ATTACK_IS_LIKELY, 55 NADM_ATTACK_IS_VERY_LIKELY, 56 NADM_ATTACK_IS_EXTREMELY_LIKELY, 57 NADM_UNKNOWN 58 }) 59 @interface Nadm {} 60 61 /** 62 * Attack is extremely unlikely. 63 * 64 * @hide 65 */ 66 @SystemApi 67 public static final int NADM_ATTACK_IS_EXTREMELY_UNLIKELY = 0; 68 69 /** 70 * Attack is very unlikely. 71 * 72 * @hide 73 */ 74 @SystemApi 75 public static final int NADM_ATTACK_IS_VERY_UNLIKELY = 1; 76 77 /** 78 * Attack is unlikely. 79 * 80 * @hide 81 */ 82 @SystemApi 83 public static final int NADM_ATTACK_IS_UNLIKELY = 2; 84 85 /** 86 * Attack is possible. 87 * 88 * @hide 89 */ 90 @SystemApi 91 public static final int NADM_ATTACK_IS_POSSIBLE = 3; 92 93 /** 94 * Attack is likely. 95 * 96 * @hide 97 */ 98 @SystemApi 99 public static final int NADM_ATTACK_IS_LIKELY = 4; 100 101 /** 102 * Attack is very likely. 103 * 104 * @hide 105 */ 106 @SystemApi 107 public static final int NADM_ATTACK_IS_VERY_LIKELY = 5; 108 109 /** 110 * Attack is extremely likely. 111 * 112 * @hide 113 */ 114 @SystemApi 115 public static final int NADM_ATTACK_IS_EXTREMELY_LIKELY = 6; 116 117 /** 118 * Unknown NADM, if a device is unable to determine a NADM value, then it shall report this. 119 * 120 * @hide 121 */ 122 @SystemApi 123 public static final int NADM_UNKNOWN = 0xFF; 124 125 private final double mMeters; 126 private final double mErrorMeters; 127 private final double mAzimuthAngle; 128 private final double mErrorAzimuthAngle; 129 private final double mAltitudeAngle; 130 private final double mErrorAltitudeAngle; 131 private final double mDelaySpreadMeters; 132 private final double mConfidenceLevel; 133 private final int mDetectedAttackLevel; 134 private final double mVelocityMetersPerSecond; 135 private final long mMeasurementTimestampNanos; 136 DistanceMeasurementResult( double meters, double errorMeters, double azimuthAngle, double errorAzimuthAngle, double altitudeAngle, double errorAltitudeAngle, double delaySpreadMeters, double confidenceLevel, @Nadm int detectedAttackLevel, double velocityMetersPerSecond, long measurementTimestampNanos)137 private DistanceMeasurementResult( 138 double meters, 139 double errorMeters, 140 double azimuthAngle, 141 double errorAzimuthAngle, 142 double altitudeAngle, 143 double errorAltitudeAngle, 144 double delaySpreadMeters, 145 double confidenceLevel, 146 @Nadm int detectedAttackLevel, 147 double velocityMetersPerSecond, 148 long measurementTimestampNanos) { 149 mMeters = meters; 150 mErrorMeters = errorMeters; 151 mAzimuthAngle = azimuthAngle; 152 mErrorAzimuthAngle = errorAzimuthAngle; 153 mAltitudeAngle = altitudeAngle; 154 mErrorAltitudeAngle = errorAltitudeAngle; 155 mDelaySpreadMeters = delaySpreadMeters; 156 mConfidenceLevel = confidenceLevel; 157 mDetectedAttackLevel = detectedAttackLevel; 158 mVelocityMetersPerSecond = velocityMetersPerSecond; 159 mMeasurementTimestampNanos = measurementTimestampNanos; 160 } 161 162 /** 163 * Distance measurement in meters. 164 * 165 * @return distance in meters 166 * @hide 167 */ 168 @SystemApi getResultMeters()169 public double getResultMeters() { 170 return mMeters; 171 } 172 173 /** 174 * Error of distance measurement in meters. 175 * 176 * <p>Must be positive. 177 * 178 * @return error of distance measurement in meters 179 * @hide 180 */ 181 @SystemApi 182 @FloatRange(from = 0.0) getErrorMeters()183 public double getErrorMeters() { 184 return mErrorMeters; 185 } 186 187 /** 188 * Azimuth Angle measurement in degrees. 189 * 190 * <p>Azimuth of remote device in horizontal coordinate system, this measured from azimuth north 191 * and increasing eastward. When the remote device in azimuth north, this angle is 0, whe the 192 * remote device in azimuth south, this angle is 180. 193 * 194 * <p>See: <a href="https://en.wikipedia.org/wiki/Horizontal_coordinate_system">Horizontal 195 * coordinate system</a>for the details 196 * 197 * <p>On an Android device, azimuth north is defined as the angle perpendicular away from the 198 * back of the device when holding it in portrait mode upright. 199 * 200 * <p>The Azimuth north is defined as the direction in which the top edge of the device is 201 * facing when it is placed flat. 202 * 203 * @return azimuth angle in degrees or Double.NaN if not available 204 * @hide 205 */ 206 @SystemApi 207 @FloatRange(from = 0.0, to = 360.0) getAzimuthAngle()208 public double getAzimuthAngle() { 209 return mAzimuthAngle; 210 } 211 212 /** 213 * Error of azimuth angle measurement in degrees. 214 * 215 * <p>Must be a positive value. 216 * 217 * @return azimuth angle measurement error in degrees or Double.NaN if not available 218 * @hide 219 */ 220 @SystemApi getErrorAzimuthAngle()221 public double getErrorAzimuthAngle() { 222 return mErrorAzimuthAngle; 223 } 224 225 /** 226 * Altitude Angle measurement in degrees. 227 * 228 * <p>Altitude of remote device in horizontal coordinate system, this is the angle between the 229 * remote device and the top edge of local device. When local device is placed flat, the angle 230 * of the zenith is 90, the angle of the nadir is -90. 231 * 232 * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system 233 * 234 * @return altitude angle in degrees or Double.NaN if not available 235 * @hide 236 */ 237 @SystemApi 238 @FloatRange(from = -90.0, to = 90.0) getAltitudeAngle()239 public double getAltitudeAngle() { 240 return mAltitudeAngle; 241 } 242 243 /** 244 * Error of altitude angle measurement in degrees. 245 * 246 * <p>Must be a positive value. 247 * 248 * @return altitude angle measurement error in degrees or Double.NaN if not available 249 * @hide 250 */ 251 @SystemApi getErrorAltitudeAngle()252 public double getErrorAltitudeAngle() { 253 return mErrorAltitudeAngle; 254 } 255 256 /** 257 * Get estimated delay spread in meters of the measured channel. This is a measure of multipath 258 * richness of the channel. 259 * 260 * @return delay spread in meters in degrees or Double.NaN if not available 261 * @hide 262 */ 263 @SystemApi getDelaySpreadMeters()264 public double getDelaySpreadMeters() { 265 return mDelaySpreadMeters; 266 } 267 268 /** 269 * Get a normalized value from 0.0 (low confidence) to 1.0 (high confidence) representing the 270 * confidence of estimated distance. 271 * 272 * @return confidence of estimated distance or Double.NaN if not available 273 * @hide 274 */ 275 @SystemApi 276 @FloatRange(from = 0.0, to = 1.0) getConfidenceLevel()277 public double getConfidenceLevel() { 278 return mConfidenceLevel; 279 } 280 281 /** 282 * Get a value that represents the chance of being attacked for the measurement. 283 * 284 * @return Nadm that represents the chance of being attacked for the measurement. 285 * @hide 286 */ 287 @SystemApi 288 @Nadm getDetectedAttackLevel()289 public int getDetectedAttackLevel() { 290 return mDetectedAttackLevel; 291 } 292 293 /** 294 * Get estimated velocity, in the direction of line between two devices, of the moving object in 295 * meters/sec. 296 * 297 * @return Estimated velocity, in the direction of line between two devices, of the moving 298 * object in meters/sec. 299 * @hide 300 */ 301 @SystemApi getVelocityMetersPerSecond()302 public double getVelocityMetersPerSecond() { 303 return mVelocityMetersPerSecond; 304 } 305 306 /** 307 * Timestamp of this distance measurement in time since boot nanos in the same namespace as 308 * {@link SystemClock#elapsedRealtimeNanos()} 309 * 310 * @return timestamp of ranging measurement in nanoseconds 311 * @hide 312 */ 313 @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS) 314 @SystemApi getMeasurementTimestampNanos()315 public long getMeasurementTimestampNanos() { 316 return mMeasurementTimestampNanos; 317 } 318 319 /** 320 * {@inheritDoc} 321 * 322 * @hide 323 */ 324 @Override describeContents()325 public int describeContents() { 326 return 0; 327 } 328 329 /** 330 * {@inheritDoc} 331 * 332 * @hide 333 */ 334 @Override writeToParcel(Parcel out, int flags)335 public void writeToParcel(Parcel out, int flags) { 336 out.writeDouble(mMeters); 337 out.writeDouble(mErrorMeters); 338 out.writeDouble(mAzimuthAngle); 339 out.writeDouble(mErrorAzimuthAngle); 340 out.writeDouble(mAltitudeAngle); 341 out.writeDouble(mErrorAltitudeAngle); 342 out.writeDouble(mDelaySpreadMeters); 343 out.writeDouble(mConfidenceLevel); 344 out.writeInt(mDetectedAttackLevel); 345 out.writeDouble(mVelocityMetersPerSecond); 346 out.writeLong(mMeasurementTimestampNanos); 347 } 348 349 /** 350 * @hide * 351 */ 352 @Override toString()353 public String toString() { 354 return "DistanceMeasurement[" 355 + "meters: " 356 + mMeters 357 + ", errorMeters: " 358 + mErrorMeters 359 + ", azimuthAngle: " 360 + mAzimuthAngle 361 + ", errorAzimuthAngle: " 362 + mErrorAzimuthAngle 363 + ", altitudeAngle: " 364 + mAltitudeAngle 365 + ", errorAltitudeAngle: " 366 + mErrorAltitudeAngle 367 + ", delaySpreadMeters: " 368 + mDelaySpreadMeters 369 + ", confidenceLevel: " 370 + mConfidenceLevel 371 + ", detectedAttackLevel: " 372 + mDetectedAttackLevel 373 + ", velocityMetersPerSecond: " 374 + mVelocityMetersPerSecond 375 + ", elapsedRealtimeNanos" 376 + mMeasurementTimestampNanos 377 + "]"; 378 } 379 380 /** A {@link Parcelable.Creator} to create {@link DistanceMeasurementResult} from parcel. */ 381 public static final @NonNull Parcelable.Creator<DistanceMeasurementResult> CREATOR = 382 new Parcelable.Creator<DistanceMeasurementResult>() { 383 @Override 384 public @NonNull DistanceMeasurementResult createFromParcel(@NonNull Parcel in) { 385 return new Builder(in.readDouble(), in.readDouble()) 386 .setAzimuthAngle(in.readDouble()) 387 .setErrorAzimuthAngle(in.readDouble()) 388 .setAltitudeAngle(in.readDouble()) 389 .setErrorAltitudeAngle(in.readDouble()) 390 .setDelaySpreadMeters(in.readDouble()) 391 .setConfidenceLevel(in.readDouble()) 392 .setDetectedAttackLevel(in.readInt()) 393 .setVelocityMetersPerSecond(in.readDouble()) 394 .setMeasurementTimestampNanos(in.readLong()) 395 .build(); 396 } 397 398 @Override 399 public @NonNull DistanceMeasurementResult[] newArray(int size) { 400 return new DistanceMeasurementResult[size]; 401 } 402 }; 403 404 /** 405 * Builder for {@link DistanceMeasurementResult}. 406 * 407 * @hide 408 */ 409 @SystemApi 410 public static final class Builder { 411 private double mMeters = Double.NaN; 412 private double mErrorMeters = Double.NaN; 413 private double mAzimuthAngle = Double.NaN; 414 private double mErrorAzimuthAngle = Double.NaN; 415 private double mAltitudeAngle = Double.NaN; 416 private double mErrorAltitudeAngle = Double.NaN; 417 private double mDelaySpreadMeters = Double.NaN; 418 private double mConfidenceLevel = Double.NaN; 419 private int mDetectedAttackLevel = NADM_UNKNOWN; 420 private double mVelocityMetersPerSecond = Double.NaN; 421 private long mMeasurementTimestampNanos = -1L; 422 423 /** 424 * Constructor of the Builder. 425 * 426 * @param meters distance in meters 427 * @param errorMeters distance error in meters 428 * @throws IllegalArgumentException if meters is NaN or error is negative or NaN 429 */ Builder( @loatRangefrom = 0.0) double meters, @FloatRange(from = 0.0) double errorMeters)430 public Builder( 431 @FloatRange(from = 0.0) double meters, @FloatRange(from = 0.0) double errorMeters) { 432 if (Double.isNaN(meters) || meters < 0.0) { 433 throw new IllegalArgumentException("meters must be >= 0.0 and not NaN: " + meters); 434 } 435 if (Double.isNaN(errorMeters) || errorMeters < 0.0) { 436 throw new IllegalArgumentException( 437 "errorMeters must be >= 0.0 and not NaN: " + errorMeters); 438 } 439 mMeters = meters; 440 mErrorMeters = errorMeters; 441 } 442 443 /** 444 * Set the azimuth angle measurement in degrees. 445 * 446 * @param angle azimuth angle in degrees 447 * @throws IllegalArgumentException if value is invalid 448 * @hide 449 */ 450 @SystemApi 451 @NonNull setAzimuthAngle(@loatRangefrom = 0.0, to = 360.0) double angle)452 public Builder setAzimuthAngle(@FloatRange(from = 0.0, to = 360.0) double angle) { 453 if (angle > 360.0 || angle < 0.0) { 454 throw new IllegalArgumentException( 455 "angle must be in the range from 0.0 to 360.0 : " + angle); 456 } 457 mAzimuthAngle = angle; 458 return this; 459 } 460 461 /** 462 * Set the azimuth angle error in degrees. 463 * 464 * @param angle azimuth angle error in degrees 465 * @throws IllegalArgumentException if value is invalid 466 * @hide 467 */ 468 @SystemApi 469 @NonNull setErrorAzimuthAngle(@loatRangefrom = 0.0, to = 360.0) double angle)470 public Builder setErrorAzimuthAngle(@FloatRange(from = 0.0, to = 360.0) double angle) { 471 if (angle > 360.0 || angle < 0.0) { 472 throw new IllegalArgumentException( 473 "error angle must be in the range from 0.0 to 360.0 : " + angle); 474 } 475 mErrorAzimuthAngle = angle; 476 return this; 477 } 478 479 /** 480 * Set the altitude angle measurement in degrees. 481 * 482 * @param angle altitude angle in degrees 483 * @throws IllegalArgumentException if value is invalid 484 * @hide 485 */ 486 @SystemApi 487 @NonNull setAltitudeAngle(@loatRangefrom = -90.0, to = 90.0) double angle)488 public Builder setAltitudeAngle(@FloatRange(from = -90.0, to = 90.0) double angle) { 489 if (angle > 90.0 || angle < -90.0) { 490 throw new IllegalArgumentException( 491 "angle must be in the range from -90.0 to 90.0 : " + angle); 492 } 493 mAltitudeAngle = angle; 494 return this; 495 } 496 497 /** 498 * Set the altitude angle error in degrees. 499 * 500 * @param angle altitude angle error in degrees 501 * @throws IllegalArgumentException if value is invalid 502 * @hide 503 */ 504 @SystemApi 505 @NonNull setErrorAltitudeAngle(@loatRangefrom = 0.0, to = 180.0) double angle)506 public Builder setErrorAltitudeAngle(@FloatRange(from = 0.0, to = 180.0) double angle) { 507 if (angle > 180.0 || angle < 0.0) { 508 throw new IllegalArgumentException( 509 "error angle must be in the range from 0.0 to 180.0 : " + angle); 510 } 511 mErrorAltitudeAngle = angle; 512 return this; 513 } 514 515 /** 516 * Set the estimated delay spread in meters. 517 * 518 * @param delaySpreadMeters estimated delay spread in meters 519 * @throws IllegalArgumentException if value is invalid 520 * @hide 521 */ 522 @SystemApi 523 @NonNull setDelaySpreadMeters(double delaySpreadMeters)524 public Builder setDelaySpreadMeters(double delaySpreadMeters) { 525 if (delaySpreadMeters < 0.0) { 526 throw new IllegalArgumentException("delaySpreadMeters must be > 0.0"); 527 } 528 mDelaySpreadMeters = delaySpreadMeters; 529 return this; 530 } 531 532 /** 533 * Set the confidence of estimated distance. 534 * 535 * @param confidenceLevel a normalized value from 0.0 (low confidence) to 1.0 (high 536 * confidence) representing the confidence of estimated distance 537 * @throws IllegalArgumentException if value is invalid 538 * @hide 539 */ 540 @SystemApi 541 @NonNull setConfidenceLevel( @loatRangefrom = 0.0, to = 1.0) double confidenceLevel)542 public Builder setConfidenceLevel( 543 @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) { 544 if (confidenceLevel > 1.0 || confidenceLevel < 0.0) { 545 throw new IllegalArgumentException( 546 "error confidenceLevel must be in the range from 0.0 to 100.0 : " 547 + confidenceLevel); 548 } 549 mConfidenceLevel = confidenceLevel; 550 return this; 551 } 552 553 /** 554 * Set the value that represents the chance of being attacked for the measurement. 555 * 556 * @param detectedAttackLevel a value that represents the chance of being attacked for the 557 * measurement. 558 * @throws IllegalArgumentException if value is invalid 559 * @hide 560 */ 561 @SystemApi 562 @NonNull setDetectedAttackLevel(@adm int detectedAttackLevel)563 public Builder setDetectedAttackLevel(@Nadm int detectedAttackLevel) { 564 switch (detectedAttackLevel) { 565 case NADM_ATTACK_IS_EXTREMELY_UNLIKELY: 566 case NADM_ATTACK_IS_VERY_UNLIKELY: 567 case NADM_ATTACK_IS_UNLIKELY: 568 case NADM_ATTACK_IS_POSSIBLE: 569 case NADM_ATTACK_IS_LIKELY: 570 case NADM_ATTACK_IS_VERY_LIKELY: 571 case NADM_ATTACK_IS_EXTREMELY_LIKELY: 572 case NADM_UNKNOWN: 573 mDetectedAttackLevel = detectedAttackLevel; 574 break; 575 default: 576 throw new IllegalArgumentException("Invalid value " + detectedAttackLevel); 577 } 578 return this; 579 } 580 581 /** 582 * Set estimated velocity, in the direction of line between two devices, of the moving 583 * object in meters/sec. 584 * 585 * @param velocityMetersPerSecond estimated velocity in meters/sec. 586 * @hide 587 */ 588 @SystemApi 589 @NonNull setVelocityMetersPerSecond(double velocityMetersPerSecond)590 public Builder setVelocityMetersPerSecond(double velocityMetersPerSecond) { 591 mVelocityMetersPerSecond = velocityMetersPerSecond; 592 return this; 593 } 594 595 /** 596 * Set the elapsed realtime in nanoseconds when the distance measurement occurred 597 * 598 * @param measurementTimestampNanos time the distance measurement occurred 599 * @hide 600 */ 601 @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS) 602 @SystemApi 603 @NonNull setMeasurementTimestampNanos(long measurementTimestampNanos)604 public Builder setMeasurementTimestampNanos(long measurementTimestampNanos) { 605 mMeasurementTimestampNanos = measurementTimestampNanos; 606 return this; 607 } 608 609 /** 610 * Builds the {@link DistanceMeasurementResult} object. 611 * 612 * @throws IllegalStateException if meters, error, or confidence are not set 613 * @hide 614 */ 615 @SystemApi 616 @NonNull build()617 public DistanceMeasurementResult build() { 618 return new DistanceMeasurementResult( 619 mMeters, 620 mErrorMeters, 621 mAzimuthAngle, 622 mErrorAzimuthAngle, 623 mAltitudeAngle, 624 mErrorAltitudeAngle, 625 mDelaySpreadMeters, 626 mConfidenceLevel, 627 mDetectedAttackLevel, 628 mVelocityMetersPerSecond, 629 mMeasurementTimestampNanos); 630 } 631 } 632 } 633