1 /* 2 * Copyright (C) 2021 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.nearby; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.bluetooth.le.ScanRecord; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.util.Arrays; 27 import java.util.Objects; 28 29 /** 30 * A data class representing scan result from Nearby Service. Scan result can come from multiple 31 * mediums like BLE, Wi-Fi Aware, and etc. A scan result consists of An encapsulation of various 32 * parameters for requesting nearby scans. 33 * 34 * <p>All scan results generated through {@link NearbyManager} are guaranteed to have a valid 35 * medium, identifier, timestamp (both UTC time and elapsed real-time since boot), and accuracy. All 36 * other parameters are optional. 37 * 38 * @hide 39 */ 40 public final class NearbyDeviceParcelable implements Parcelable { 41 42 /** Used to read a NearbyDeviceParcelable from a Parcel. */ 43 @NonNull 44 public static final Creator<NearbyDeviceParcelable> CREATOR = 45 new Creator<NearbyDeviceParcelable>() { 46 @Override 47 public NearbyDeviceParcelable createFromParcel(Parcel in) { 48 Builder builder = new Builder(); 49 builder.setDeviceId(in.readLong()); 50 builder.setScanType(in.readInt()); 51 if (in.readInt() == 1) { 52 builder.setName(in.readString()); 53 } 54 builder.setMedium(in.readInt()); 55 builder.setTxPower(in.readInt()); 56 builder.setRssi(in.readInt()); 57 builder.setAction(in.readInt()); 58 builder.setPublicCredential( 59 in.readParcelable( 60 PublicCredential.class.getClassLoader(), 61 PublicCredential.class)); 62 if (in.readInt() == 1) { 63 builder.setFastPairModelId(in.readString()); 64 } 65 if (in.readInt() == 1) { 66 builder.setBluetoothAddress(in.readString()); 67 } 68 if (in.readInt() == 1) { 69 int dataLength = in.readInt(); 70 byte[] data = new byte[dataLength]; 71 in.readByteArray(data); 72 builder.setData(data); 73 } 74 if (in.readInt() == 1) { 75 int saltLength = in.readInt(); 76 byte[] salt = new byte[saltLength]; 77 in.readByteArray(salt); 78 builder.setData(salt); 79 } 80 if (in.readInt() == 1) { 81 builder.setPresenceDevice(in.readParcelable( 82 PresenceDevice.class.getClassLoader(), 83 PresenceDevice.class)); 84 } 85 if (in.readInt() == 1) { 86 int encryptionKeyTagLength = in.readInt(); 87 byte[] keyTag = new byte[encryptionKeyTagLength]; 88 in.readByteArray(keyTag); 89 builder.setData(keyTag); 90 } 91 return builder.build(); 92 } 93 94 @Override 95 public NearbyDeviceParcelable[] newArray(int size) { 96 return new NearbyDeviceParcelable[size]; 97 } 98 }; 99 100 private final long mDeviceId; 101 @ScanRequest.ScanType int mScanType; 102 @Nullable private final String mName; 103 @NearbyDevice.Medium private final int mMedium; 104 private final int mTxPower; 105 private final int mRssi; 106 private final int mAction; 107 private final PublicCredential mPublicCredential; 108 @Nullable private final String mBluetoothAddress; 109 @Nullable private final String mFastPairModelId; 110 @Nullable private final byte[] mData; 111 @Nullable private final byte[] mSalt; 112 @Nullable private final PresenceDevice mPresenceDevice; 113 @Nullable private final byte[] mEncryptionKeyTag; 114 NearbyDeviceParcelable( long deviceId, @ScanRequest.ScanType int scanType, @Nullable String name, int medium, int TxPower, int rssi, int action, PublicCredential publicCredential, @Nullable String fastPairModelId, @Nullable String bluetoothAddress, @Nullable byte[] data, @Nullable byte[] salt, @Nullable PresenceDevice presenceDevice, @Nullable byte[] encryptionKeyTag)115 private NearbyDeviceParcelable( 116 long deviceId, 117 @ScanRequest.ScanType int scanType, 118 @Nullable String name, 119 int medium, 120 int TxPower, 121 int rssi, 122 int action, 123 PublicCredential publicCredential, 124 @Nullable String fastPairModelId, 125 @Nullable String bluetoothAddress, 126 @Nullable byte[] data, 127 @Nullable byte[] salt, 128 @Nullable PresenceDevice presenceDevice, 129 @Nullable byte[] encryptionKeyTag) { 130 mDeviceId = deviceId; 131 mScanType = scanType; 132 mName = name; 133 mMedium = medium; 134 mTxPower = TxPower; 135 mRssi = rssi; 136 mAction = action; 137 mPublicCredential = publicCredential; 138 mFastPairModelId = fastPairModelId; 139 mBluetoothAddress = bluetoothAddress; 140 mData = data; 141 mSalt = salt; 142 mPresenceDevice = presenceDevice; 143 mEncryptionKeyTag = encryptionKeyTag; 144 } 145 146 /** No special parcel contents. */ 147 @Override describeContents()148 public int describeContents() { 149 return 0; 150 } 151 152 /** 153 * Flatten this NearbyDeviceParcelable in to a Parcel. 154 * 155 * @param dest The Parcel in which the object should be written. 156 * @param flags Additional flags about how the object should be written. 157 */ 158 @Override writeToParcel(@onNull Parcel dest, int flags)159 public void writeToParcel(@NonNull Parcel dest, int flags) { 160 dest.writeLong(mDeviceId); 161 dest.writeInt(mScanType); 162 dest.writeInt(mName == null ? 0 : 1); 163 if (mName != null) { 164 dest.writeString(mName); 165 } 166 dest.writeInt(mMedium); 167 dest.writeInt(mTxPower); 168 dest.writeInt(mRssi); 169 dest.writeInt(mAction); 170 dest.writeParcelable(mPublicCredential, flags); 171 dest.writeInt(mFastPairModelId == null ? 0 : 1); 172 if (mFastPairModelId != null) { 173 dest.writeString(mFastPairModelId); 174 } 175 dest.writeInt(mBluetoothAddress == null ? 0 : 1); 176 if (mBluetoothAddress != null) { 177 dest.writeString(mBluetoothAddress); 178 } 179 dest.writeInt(mData == null ? 0 : 1); 180 if (mData != null) { 181 dest.writeInt(mData.length); 182 dest.writeByteArray(mData); 183 } 184 dest.writeInt(mSalt == null ? 0 : 1); 185 if (mSalt != null) { 186 dest.writeInt(mSalt.length); 187 dest.writeByteArray(mSalt); 188 } 189 dest.writeInt(mPresenceDevice == null ? 0 : 1); 190 if (mPresenceDevice != null) { 191 dest.writeParcelable(mPresenceDevice, /* parcelableFlags= */ 0); 192 } 193 dest.writeInt(mEncryptionKeyTag == null ? 0 : 1); 194 if (mEncryptionKeyTag != null) { 195 dest.writeInt(mEncryptionKeyTag.length); 196 dest.writeByteArray(mEncryptionKeyTag); 197 } 198 } 199 200 /** Returns a string representation of this ScanRequest. */ 201 @Override toString()202 public String toString() { 203 return "NearbyDeviceParcelable[" 204 + "deviceId=" 205 + mDeviceId 206 + ", scanType=" 207 + mScanType 208 + ", name=" 209 + mName 210 + ", medium=" 211 + NearbyDevice.mediumToString(mMedium) 212 + ", txPower=" 213 + mTxPower 214 + ", rssi=" 215 + mRssi 216 + ", action=" 217 + mAction 218 + ", bluetoothAddress=" 219 + mBluetoothAddress 220 + ", fastPairModelId=" 221 + mFastPairModelId 222 + ", data=" 223 + Arrays.toString(mData) 224 + ", salt=" 225 + Arrays.toString(mSalt) 226 + "]"; 227 } 228 229 @Override equals(Object other)230 public boolean equals(Object other) { 231 if (other instanceof NearbyDeviceParcelable) { 232 NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other; 233 return mDeviceId == otherNearbyDeviceParcelable.mDeviceId 234 && mScanType == otherNearbyDeviceParcelable.mScanType 235 && (Objects.equals(mName, otherNearbyDeviceParcelable.mName)) 236 && (mMedium == otherNearbyDeviceParcelable.mMedium) 237 && (mTxPower == otherNearbyDeviceParcelable.mTxPower) 238 && (mRssi == otherNearbyDeviceParcelable.mRssi) 239 && (mAction == otherNearbyDeviceParcelable.mAction) 240 && (Objects.equals( 241 mPublicCredential, otherNearbyDeviceParcelable.mPublicCredential)) 242 && (Objects.equals( 243 mBluetoothAddress, otherNearbyDeviceParcelable.mBluetoothAddress)) 244 && (Objects.equals( 245 mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId)) 246 && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData)) 247 && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt)) 248 && (Objects.equals( 249 mPresenceDevice, otherNearbyDeviceParcelable.mPresenceDevice)) 250 && (Arrays.equals( 251 mEncryptionKeyTag, otherNearbyDeviceParcelable.mEncryptionKeyTag)); 252 } 253 return false; 254 } 255 256 @Override hashCode()257 public int hashCode() { 258 return Objects.hash( 259 mDeviceId, 260 mScanType, 261 mName, 262 mMedium, 263 mRssi, 264 mAction, 265 mPublicCredential.hashCode(), 266 mBluetoothAddress, 267 mFastPairModelId, 268 Arrays.hashCode(mData), 269 Arrays.hashCode(mSalt), 270 mPresenceDevice, 271 Arrays.hashCode(mEncryptionKeyTag)); 272 } 273 274 /** 275 * The id of the device. 276 * <p>This id is not a hardware id. It may rotate based on the remote device's broadcasts. 277 * 278 * @hide 279 */ getDeviceId()280 public long getDeviceId() { 281 return mDeviceId; 282 } 283 284 /** 285 * Returns the type of the scan. 286 * 287 * @hide 288 */ 289 @ScanRequest.ScanType getScanType()290 public int getScanType() { 291 return mScanType; 292 } 293 294 /** 295 * Gets the name of the NearbyDeviceParcelable. Returns {@code null} If there is no name. 296 * 297 * Used in Fast Pair. 298 */ 299 @Nullable getName()300 public String getName() { 301 return mName; 302 } 303 304 /** 305 * Gets the {@link android.nearby.NearbyDevice.Medium} of the NearbyDeviceParcelable over which 306 * it is discovered. 307 * 308 * Used in Fast Pair and Nearby Presence. 309 */ 310 @NearbyDevice.Medium getMedium()311 public int getMedium() { 312 return mMedium; 313 } 314 315 /** 316 * Gets the transmission power in dBm. 317 * 318 * Used in Fast Pair. 319 * 320 * @hide 321 */ 322 @IntRange(from = -127, to = 126) getTxPower()323 public int getTxPower() { 324 return mTxPower; 325 } 326 327 /** 328 * Gets the received signal strength in dBm. 329 * 330 * Used in Fast Pair and Nearby Presence. 331 */ 332 @IntRange(from = -127, to = 126) getRssi()333 public int getRssi() { 334 return mRssi; 335 } 336 337 /** 338 * Gets the Action. 339 * 340 * Used in Nearby Presence. 341 * 342 * @hide 343 */ 344 @IntRange(from = -127, to = 126) getAction()345 public int getAction() { 346 return mAction; 347 } 348 349 /** 350 * Gets the public credential. 351 * 352 * Used in Nearby Presence. 353 * 354 * @hide 355 */ 356 @NonNull getPublicCredential()357 public PublicCredential getPublicCredential() { 358 return mPublicCredential; 359 } 360 361 /** 362 * Gets the Fast Pair identifier. Returns {@code null} if there is no Model ID or this is not a 363 * Fast Pair device. 364 * 365 * Used in Fast Pair. 366 */ 367 @Nullable getFastPairModelId()368 public String getFastPairModelId() { 369 return mFastPairModelId; 370 } 371 372 /** 373 * Gets the Bluetooth device hardware address. Returns {@code null} if the device is not 374 * discovered by Bluetooth. 375 * 376 * Used in Fast Pair. 377 */ 378 @Nullable getBluetoothAddress()379 public String getBluetoothAddress() { 380 return mBluetoothAddress; 381 } 382 383 /** 384 * Gets the raw data from the scanning. 385 * Returns {@code null} if there is no extra data or this is not a Fast Pair device. 386 * 387 * Used in Fast Pair. 388 */ 389 @Nullable getData()390 public byte[] getData() { 391 return mData; 392 } 393 394 /** 395 * Gets the salt in the advertisement from the Nearby Presence device. 396 * Returns {@code null} if this is not a Nearby Presence device. 397 * 398 * Used in Nearby Presence. 399 */ 400 @Nullable getSalt()401 public byte[] getSalt() { 402 return mSalt; 403 } 404 405 /** 406 * Gets the {@link PresenceDevice} Nearby Presence device. This field is 407 * for Fast Pair client only. 408 */ 409 @Nullable getPresenceDevice()410 public PresenceDevice getPresenceDevice() { 411 return mPresenceDevice; 412 } 413 414 /** 415 * Gets the encryption key tag calculated from advertisement 416 * Returns {@code null} if the data is not encrypted or this is not a Presence device. 417 * 418 * Used in Presence. 419 */ 420 @Nullable getEncryptionKeyTag()421 public byte[] getEncryptionKeyTag() { 422 return mEncryptionKeyTag; 423 } 424 425 /** Builder class for {@link NearbyDeviceParcelable}. */ 426 public static final class Builder { 427 private long mDeviceId = -1; 428 @Nullable private String mName; 429 @NearbyDevice.Medium private int mMedium; 430 private int mTxPower; 431 private int mRssi; 432 private int mAction; 433 private PublicCredential mPublicCredential; 434 @ScanRequest.ScanType int mScanType; 435 @Nullable private String mFastPairModelId; 436 @Nullable private String mBluetoothAddress; 437 @Nullable private byte[] mData; 438 @Nullable private byte[] mSalt; 439 @Nullable private PresenceDevice mPresenceDevice; 440 @Nullable private byte[] mEncryptionKeyTag; 441 442 /** Sets the id of the device. */ setDeviceId(long deviceId)443 public Builder setDeviceId(long deviceId) { 444 this.mDeviceId = deviceId; 445 return this; 446 } 447 448 /** 449 * Sets the scan type of the NearbyDeviceParcelable. 450 * 451 * @hide 452 */ setScanType(@canRequest.ScanType int scanType)453 public Builder setScanType(@ScanRequest.ScanType int scanType) { 454 mScanType = scanType; 455 return this; 456 } 457 458 /** 459 * Sets the name of the scanned device. 460 * 461 * @param name The local name of the scanned device. 462 */ 463 @NonNull setName(@ullable String name)464 public Builder setName(@Nullable String name) { 465 mName = name; 466 return this; 467 } 468 469 /** 470 * Sets the medium over which the device is discovered. 471 * 472 * @param medium The {@link NearbyDevice.Medium} over which the device is discovered. 473 */ 474 @NonNull setMedium(@earbyDevice.Medium int medium)475 public Builder setMedium(@NearbyDevice.Medium int medium) { 476 mMedium = medium; 477 return this; 478 } 479 480 /** 481 * Sets the transmission power of the discovered device. 482 * 483 * @param txPower The transmission power in dBm. 484 * @hide 485 */ 486 @NonNull setTxPower(int txPower)487 public Builder setTxPower(int txPower) { 488 mTxPower = txPower; 489 return this; 490 } 491 492 /** 493 * Sets the RSSI between scanned device and the discovered device. 494 * 495 * @param rssi The received signal strength in dBm. 496 */ 497 @NonNull setRssi(@ntRangefrom = -127, to = 126) int rssi)498 public Builder setRssi(@IntRange(from = -127, to = 126) int rssi) { 499 mRssi = rssi; 500 return this; 501 } 502 503 /** 504 * Sets the action from the discovered device. 505 * 506 * @param action The action of the discovered device. 507 * @hide 508 */ 509 @NonNull setAction(int action)510 public Builder setAction(int action) { 511 mAction = action; 512 return this; 513 } 514 515 /** 516 * Sets the public credential of the discovered device. 517 * 518 * @param publicCredential The public credential. 519 * @hide 520 */ 521 @NonNull setPublicCredential(@onNull PublicCredential publicCredential)522 public Builder setPublicCredential(@NonNull PublicCredential publicCredential) { 523 mPublicCredential = publicCredential; 524 return this; 525 } 526 527 /** 528 * Sets the Fast Pair model Id. 529 * 530 * @param fastPairModelId Fast Pair device identifier. 531 */ 532 @NonNull setFastPairModelId(@ullable String fastPairModelId)533 public Builder setFastPairModelId(@Nullable String fastPairModelId) { 534 mFastPairModelId = fastPairModelId; 535 return this; 536 } 537 538 /** 539 * Sets the bluetooth address. 540 * 541 * @param bluetoothAddress The hardware address of the bluetooth device. 542 */ 543 @NonNull setBluetoothAddress(@ullable String bluetoothAddress)544 public Builder setBluetoothAddress(@Nullable String bluetoothAddress) { 545 mBluetoothAddress = bluetoothAddress; 546 return this; 547 } 548 549 /** 550 * Sets the scanned raw data. 551 * 552 * @param data raw data scanned, like {@link ScanRecord#getServiceData()} if scanned by 553 * Bluetooth. 554 */ 555 @NonNull setData(@ullable byte[] data)556 public Builder setData(@Nullable byte[] data) { 557 mData = data; 558 return this; 559 } 560 561 /** 562 * Sets the encryption key tag calculated from the advertisement. 563 * 564 * @param encryptionKeyTag calculated from identity scanned from the advertisement 565 */ 566 @NonNull setEncryptionKeyTag(@ullable byte[] encryptionKeyTag)567 public Builder setEncryptionKeyTag(@Nullable byte[] encryptionKeyTag) { 568 mEncryptionKeyTag = encryptionKeyTag; 569 return this; 570 } 571 572 /** 573 * Sets the slat in the advertisement from the Nearby Presence device. 574 * 575 * @param salt in the advertisement from the Nearby Presence device. 576 */ 577 @NonNull setSalt(@ullable byte[] salt)578 public Builder setSalt(@Nullable byte[] salt) { 579 mSalt = salt; 580 return this; 581 } 582 583 /** 584 * Sets the {@link PresenceDevice} if there is any. 585 * The current {@link NearbyDeviceParcelable} can be seen as the wrapper of the 586 * {@link PresenceDevice}. 587 */ 588 @Nullable setPresenceDevice(@ullable PresenceDevice presenceDevice)589 public Builder setPresenceDevice(@Nullable PresenceDevice presenceDevice) { 590 mPresenceDevice = presenceDevice; 591 return this; 592 } 593 594 /** Builds a ScanResult. */ 595 @NonNull build()596 public NearbyDeviceParcelable build() { 597 return new NearbyDeviceParcelable( 598 mDeviceId, 599 mScanType, 600 mName, 601 mMedium, 602 mTxPower, 603 mRssi, 604 mAction, 605 mPublicCredential, 606 mFastPairModelId, 607 mBluetoothAddress, 608 mData, 609 mSalt, 610 mPresenceDevice, 611 mEncryptionKeyTag); 612 } 613 } 614 } 615