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