1 /* 2 * Copyright (C) 2014 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 static java.util.Objects.requireNonNull; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothDevice.AddressType; 28 import android.bluetooth.BluetoothStatusCodes; 29 import android.bluetooth.annotations.RequiresBluetoothScanPermission; 30 import android.bluetooth.le.ScanRecord.AdvertisingDataType; 31 import android.os.Parcel; 32 import android.os.ParcelUuid; 33 import android.os.Parcelable; 34 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.Objects; 38 import java.util.UUID; 39 40 /** 41 * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to 42 * restrict scan results to only those that are of interest to them. 43 * 44 * <p>Current filtering on the following fields are supported: 45 * <li>Service UUIDs which identify the bluetooth gatt services running on the device. 46 * <li>Name of remote Bluetooth LE device. 47 * <li>Mac address of the remote device. 48 * <li>Service data which is the data associated with a service. 49 * <li>Manufacturer specific data which is the data associated with a particular manufacturer. 50 * <li>Advertising data type and corresponding data. 51 * 52 * @see ScanResult 53 * @see BluetoothLeScanner 54 */ 55 public final class ScanFilter implements Parcelable { 56 57 @Nullable private final String mDeviceName; 58 59 @Nullable private final String mDeviceAddress; 60 61 private final @AddressType int mAddressType; 62 63 @Nullable private final byte[] mIrk; 64 65 @Nullable private final ParcelUuid mServiceUuid; 66 @Nullable private final ParcelUuid mServiceUuidMask; 67 68 @Nullable private final ParcelUuid mServiceSolicitationUuid; 69 @Nullable private final ParcelUuid mServiceSolicitationUuidMask; 70 71 @Nullable private final ParcelUuid mServiceDataUuid; 72 @Nullable private final byte[] mServiceData; 73 @Nullable private final byte[] mServiceDataMask; 74 75 private final int mManufacturerId; 76 @Nullable private final byte[] mManufacturerData; 77 @Nullable private final byte[] mManufacturerDataMask; 78 79 private int mAdvertisingDataType = ScanRecord.DATA_TYPE_NONE; 80 @Nullable private final byte[] mAdvertisingData; 81 @Nullable private final byte[] mAdvertisingDataMask; 82 83 @Nullable private final TransportBlockFilter mTransportBlockFilter; 84 85 /** @hide */ 86 public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); 87 ScanFilter( String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask, ParcelUuid solicitationUuid, ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, @AddressType int addressType, @Nullable byte[] irk, int advertisingDataType, @Nullable byte[] advertisingData, @Nullable byte[] advertisingDataMask, @Nullable TransportBlockFilter transportBlockFilter)88 private ScanFilter( 89 String name, 90 String deviceAddress, 91 ParcelUuid uuid, 92 ParcelUuid uuidMask, 93 ParcelUuid solicitationUuid, 94 ParcelUuid solicitationUuidMask, 95 ParcelUuid serviceDataUuid, 96 byte[] serviceData, 97 byte[] serviceDataMask, 98 int manufacturerId, 99 byte[] manufacturerData, 100 byte[] manufacturerDataMask, 101 @AddressType int addressType, 102 @Nullable byte[] irk, 103 int advertisingDataType, 104 @Nullable byte[] advertisingData, 105 @Nullable byte[] advertisingDataMask, 106 @Nullable TransportBlockFilter transportBlockFilter) { 107 mDeviceName = name; 108 mServiceUuid = uuid; 109 mServiceUuidMask = uuidMask; 110 mServiceSolicitationUuid = solicitationUuid; 111 mServiceSolicitationUuidMask = solicitationUuidMask; 112 mDeviceAddress = deviceAddress; 113 mServiceDataUuid = serviceDataUuid; 114 mServiceData = serviceData; 115 mServiceDataMask = serviceDataMask; 116 mManufacturerId = manufacturerId; 117 mManufacturerData = manufacturerData; 118 mManufacturerDataMask = manufacturerDataMask; 119 mAddressType = addressType; 120 mIrk = irk; 121 mAdvertisingDataType = advertisingDataType; 122 mAdvertisingData = advertisingData; 123 mAdvertisingDataMask = advertisingDataMask; 124 mTransportBlockFilter = transportBlockFilter; 125 } 126 127 @Override describeContents()128 public int describeContents() { 129 return 0; 130 } 131 132 @Override writeToParcel(Parcel dest, int flags)133 public void writeToParcel(Parcel dest, int flags) { 134 dest.writeInt(mDeviceName == null ? 0 : 1); 135 if (mDeviceName != null) { 136 dest.writeString(mDeviceName); 137 } 138 dest.writeInt(mDeviceAddress == null ? 0 : 1); 139 if (mDeviceAddress != null) { 140 dest.writeString(mDeviceAddress); 141 } 142 dest.writeInt(mServiceUuid == null ? 0 : 1); 143 if (mServiceUuid != null) { 144 dest.writeParcelable(mServiceUuid, flags); 145 dest.writeInt(mServiceUuidMask == null ? 0 : 1); 146 if (mServiceUuidMask != null) { 147 dest.writeParcelable(mServiceUuidMask, flags); 148 } 149 } 150 dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1); 151 if (mServiceSolicitationUuid != null) { 152 dest.writeParcelable(mServiceSolicitationUuid, flags); 153 dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1); 154 if (mServiceSolicitationUuidMask != null) { 155 dest.writeParcelable(mServiceSolicitationUuidMask, flags); 156 } 157 } 158 dest.writeInt(mServiceDataUuid == null ? 0 : 1); 159 if (mServiceDataUuid != null) { 160 dest.writeParcelable(mServiceDataUuid, flags); 161 dest.writeInt(mServiceData == null ? 0 : 1); 162 if (mServiceData != null) { 163 dest.writeInt(mServiceData.length); 164 dest.writeByteArray(mServiceData); 165 166 dest.writeInt(mServiceDataMask == null ? 0 : 1); 167 if (mServiceDataMask != null) { 168 dest.writeInt(mServiceDataMask.length); 169 dest.writeByteArray(mServiceDataMask); 170 } 171 } 172 } 173 dest.writeInt(mManufacturerId); 174 dest.writeInt(mManufacturerData == null ? 0 : 1); 175 if (mManufacturerData != null) { 176 dest.writeInt(mManufacturerData.length); 177 dest.writeByteArray(mManufacturerData); 178 179 dest.writeInt(mManufacturerDataMask == null ? 0 : 1); 180 if (mManufacturerDataMask != null) { 181 dest.writeInt(mManufacturerDataMask.length); 182 dest.writeByteArray(mManufacturerDataMask); 183 } 184 } 185 186 // IRK 187 if (mDeviceAddress != null) { 188 dest.writeInt(mAddressType); 189 dest.writeInt(mIrk == null ? 0 : 1); 190 if (mIrk != null) { 191 dest.writeByteArray(Arrays.copyOfRange(mIrk, 0, 16)); 192 } 193 } 194 195 // Advertising data type filter 196 dest.writeInt(mAdvertisingDataType); 197 dest.writeInt(mAdvertisingData == null ? 0 : 1); 198 if (mAdvertisingData != null) { 199 dest.writeInt(mAdvertisingData.length); 200 dest.writeByteArray(mAdvertisingData); 201 202 dest.writeInt(mAdvertisingDataMask == null ? 0 : 1); 203 if (mAdvertisingDataMask != null) { 204 dest.writeInt(mAdvertisingDataMask.length); 205 dest.writeByteArray(mAdvertisingDataMask); 206 } 207 } 208 209 dest.writeInt(mTransportBlockFilter == null ? 0 : 1); 210 if (mTransportBlockFilter != null) { 211 dest.writeTypedObject(mTransportBlockFilter, 0); 212 } 213 } 214 215 /** A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. */ 216 public static final @android.annotation.NonNull Creator<ScanFilter> CREATOR = 217 new Creator<ScanFilter>() { 218 219 @Override 220 public ScanFilter[] newArray(int size) { 221 return new ScanFilter[size]; 222 } 223 224 @Override 225 public ScanFilter createFromParcel(Parcel in) { 226 Builder builder = new Builder(); 227 if (in.readInt() == 1) { 228 builder.setDeviceName(in.readString()); 229 } 230 String address = null; 231 // If we have a non-null address 232 if (in.readInt() == 1) { 233 address = in.readString(); 234 } 235 if (in.readInt() == 1) { 236 ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); 237 builder.setServiceUuid(uuid); 238 if (in.readInt() == 1) { 239 ParcelUuid uuidMask = 240 in.readParcelable(ParcelUuid.class.getClassLoader()); 241 builder.setServiceUuid(uuid, uuidMask); 242 } 243 } 244 if (in.readInt() == 1) { 245 ParcelUuid solicitationUuid = 246 in.readParcelable(ParcelUuid.class.getClassLoader()); 247 builder.setServiceSolicitationUuid(solicitationUuid); 248 if (in.readInt() == 1) { 249 ParcelUuid solicitationUuidMask = 250 in.readParcelable(ParcelUuid.class.getClassLoader()); 251 builder.setServiceSolicitationUuid( 252 solicitationUuid, solicitationUuidMask); 253 } 254 } 255 if (in.readInt() == 1) { 256 ParcelUuid serviceDataUuid = 257 in.readParcelable(ParcelUuid.class.getClassLoader()); 258 if (in.readInt() == 1) { 259 int serviceDataLength = in.readInt(); 260 byte[] serviceData = new byte[serviceDataLength]; 261 in.readByteArray(serviceData); 262 if (in.readInt() == 0) { 263 builder.setServiceData(serviceDataUuid, serviceData); 264 } else { 265 int serviceDataMaskLength = in.readInt(); 266 byte[] serviceDataMask = new byte[serviceDataMaskLength]; 267 in.readByteArray(serviceDataMask); 268 builder.setServiceData( 269 serviceDataUuid, serviceData, serviceDataMask); 270 } 271 } 272 } 273 274 int manufacturerId = in.readInt(); 275 if (in.readInt() == 1) { 276 int manufacturerDataLength = in.readInt(); 277 byte[] manufacturerData = new byte[manufacturerDataLength]; 278 in.readByteArray(manufacturerData); 279 if (in.readInt() == 0) { 280 builder.setManufacturerData(manufacturerId, manufacturerData); 281 } else { 282 int manufacturerDataMaskLength = in.readInt(); 283 byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; 284 in.readByteArray(manufacturerDataMask); 285 builder.setManufacturerData( 286 manufacturerId, manufacturerData, manufacturerDataMask); 287 } 288 } 289 290 // IRK 291 if (address != null) { 292 final int addressType = in.readInt(); 293 if (in.readInt() == 1) { 294 final byte[] irk = new byte[16]; 295 in.readByteArray(irk); 296 builder.setDeviceAddress(address, addressType, irk); 297 } else { 298 builder.setDeviceAddress(address, addressType); 299 } 300 } 301 302 // Advertising data type 303 int advertisingDataType = in.readInt(); 304 if (in.readInt() == 1) { 305 byte[] advertisingData = null; 306 byte[] advertisingDataMask = null; 307 308 int advertisingDataLength = in.readInt(); 309 advertisingData = new byte[advertisingDataLength]; 310 in.readByteArray(advertisingData); 311 if (in.readInt() == 1) { 312 int advertisingDataMaskLength = in.readInt(); 313 advertisingDataMask = new byte[advertisingDataMaskLength]; 314 in.readByteArray(advertisingDataMask); 315 } 316 builder.setAdvertisingDataTypeWithData( 317 advertisingDataType, advertisingData, advertisingDataMask); 318 } 319 320 if (in.readInt() == 1) { 321 builder.setTransportBlockFilter( 322 in.readTypedObject(TransportBlockFilter.CREATOR)); 323 } 324 325 return builder.build(); 326 } 327 }; 328 329 /** Returns the filter set the device name field of Bluetooth advertisement data. */ 330 @Nullable getDeviceName()331 public String getDeviceName() { 332 return mDeviceName; 333 } 334 335 /** Returns the filter set on the service uuid. */ 336 @Nullable getServiceUuid()337 public ParcelUuid getServiceUuid() { 338 return mServiceUuid; 339 } 340 341 @Nullable getServiceUuidMask()342 public ParcelUuid getServiceUuidMask() { 343 return mServiceUuidMask; 344 } 345 346 /** Returns the filter set on the service Solicitation uuid. */ 347 @Nullable getServiceSolicitationUuid()348 public ParcelUuid getServiceSolicitationUuid() { 349 return mServiceSolicitationUuid; 350 } 351 352 /** Returns the filter set on the service Solicitation uuid mask. */ 353 @Nullable getServiceSolicitationUuidMask()354 public ParcelUuid getServiceSolicitationUuidMask() { 355 return mServiceSolicitationUuidMask; 356 } 357 358 @Nullable getDeviceAddress()359 public String getDeviceAddress() { 360 return mDeviceAddress; 361 } 362 363 /** @hide */ 364 @SystemApi getAddressType()365 public @AddressType int getAddressType() { 366 return mAddressType; 367 } 368 369 /** @hide */ 370 @SystemApi 371 @Nullable getIrk()372 public byte[] getIrk() { 373 return mIrk; 374 } 375 376 @Nullable getServiceData()377 public byte[] getServiceData() { 378 return mServiceData; 379 } 380 381 @Nullable getServiceDataMask()382 public byte[] getServiceDataMask() { 383 return mServiceDataMask; 384 } 385 386 @Nullable getServiceDataUuid()387 public ParcelUuid getServiceDataUuid() { 388 return mServiceDataUuid; 389 } 390 391 /** Returns the manufacturer id. -1 if the manufacturer filter is not set. */ getManufacturerId()392 public int getManufacturerId() { 393 return mManufacturerId; 394 } 395 396 @Nullable getManufacturerData()397 public byte[] getManufacturerData() { 398 return mManufacturerData; 399 } 400 401 @Nullable getManufacturerDataMask()402 public byte[] getManufacturerDataMask() { 403 return mManufacturerDataMask; 404 } 405 406 /** 407 * Return filter information for a transport block in Transport Discovery Service advertisement. 408 * 409 * @hide 410 */ 411 @SystemApi 412 @Nullable getTransportBlockFilter()413 public TransportBlockFilter getTransportBlockFilter() { 414 return mTransportBlockFilter; 415 } 416 417 /** 418 * Returns the advertising data type of this filter. Returns {@link ScanRecord#DATA_TYPE_NONE} 419 * if the type is not set. The values of advertising data type are defined in the Bluetooth 420 * Generic Access Profile (https://www.bluetooth.com/specifications/assigned-numbers/) 421 */ 422 @AdvertisingDataType getAdvertisingDataType()423 public int getAdvertisingDataType() { 424 return mAdvertisingDataType; 425 } 426 427 /** Returns the advertising data of this filter. */ getAdvertisingData()428 public @Nullable byte[] getAdvertisingData() { 429 return mAdvertisingData; 430 } 431 432 /** Returns the advertising data mask of this filter. */ getAdvertisingDataMask()433 public @Nullable byte[] getAdvertisingDataMask() { 434 return mAdvertisingDataMask; 435 } 436 437 /** 438 * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match 439 * if it matches all the field filters. 440 */ matches(ScanResult scanResult)441 public boolean matches(ScanResult scanResult) { 442 if (scanResult == null) { 443 return false; 444 } 445 BluetoothDevice device = scanResult.getDevice(); 446 // Device match. 447 if (mDeviceAddress != null 448 && (device == null || !mDeviceAddress.equals(device.getAddress()))) { 449 return false; 450 } 451 452 ScanRecord scanRecord = scanResult.getScanRecord(); 453 454 // Scan record is null but there exist filters on it. 455 if (scanRecord == null 456 && (mDeviceName != null 457 || mServiceUuid != null 458 || mManufacturerData != null 459 || mServiceData != null 460 || mServiceSolicitationUuid != null 461 || mAdvertisingData != null)) { 462 return false; 463 } 464 465 // Local name match. 466 if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) { 467 return false; 468 } 469 470 // UUID match. 471 if (mServiceUuid != null 472 && !matchesServiceUuids( 473 mServiceUuid, mServiceUuidMask, scanRecord.getServiceUuids())) { 474 return false; 475 } 476 477 // solicitation UUID match. 478 if (mServiceSolicitationUuid != null 479 && !matchesServiceSolicitationUuids( 480 mServiceSolicitationUuid, 481 mServiceSolicitationUuidMask, 482 scanRecord.getServiceSolicitationUuids())) { 483 return false; 484 } 485 486 // Service data match 487 if (mServiceDataUuid != null) { 488 if (!matchesPartialData( 489 mServiceData, mServiceDataMask, scanRecord.getServiceData(mServiceDataUuid))) { 490 return false; 491 } 492 } 493 494 // Manufacturer data match. 495 if (mManufacturerId >= 0) { 496 if (!matchesPartialData( 497 mManufacturerData, 498 mManufacturerDataMask, 499 scanRecord.getManufacturerSpecificData(mManufacturerId))) { 500 return false; 501 } 502 } 503 504 // Advertising data type match 505 if (mAdvertisingDataType > 0) { 506 byte[] advertisingData = scanRecord.getAdvertisingDataMap().get(mAdvertisingDataType); 507 if (advertisingData == null 508 || !matchesPartialData( 509 mAdvertisingData, mAdvertisingDataMask, advertisingData)) { 510 return false; 511 } 512 } 513 514 // Transport Discovery data match 515 if (mTransportBlockFilter != null && !mTransportBlockFilter.matches(scanResult)) { 516 return false; 517 } 518 519 // All filters match. 520 return true; 521 } 522 523 /** 524 * Check if the uuid pattern is contained in a list of parcel uuids. 525 * 526 * @hide 527 */ matchesServiceUuids( ParcelUuid uuid, ParcelUuid parcelUuidMask, List<ParcelUuid> uuids)528 public static boolean matchesServiceUuids( 529 ParcelUuid uuid, ParcelUuid parcelUuidMask, List<ParcelUuid> uuids) { 530 if (uuid == null) { 531 return true; 532 } 533 if (uuids == null) { 534 return false; 535 } 536 537 for (ParcelUuid parcelUuid : uuids) { 538 UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); 539 if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { 540 return true; 541 } 542 } 543 return false; 544 } 545 546 // Check if the uuid pattern matches the particular service uuid. matchesServiceUuid(UUID uuid, UUID mask, UUID data)547 private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { 548 return BluetoothLeUtils.maskedEquals(data, uuid, mask); 549 } 550 551 /** Check if the solicitation uuid pattern is contained in a list of parcel uuids. */ matchesServiceSolicitationUuids( ParcelUuid solicitationUuid, ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids)552 private static boolean matchesServiceSolicitationUuids( 553 ParcelUuid solicitationUuid, 554 ParcelUuid parcelSolicitationUuidMask, 555 List<ParcelUuid> solicitationUuids) { 556 if (solicitationUuid == null) { 557 return true; 558 } 559 if (solicitationUuids == null) { 560 return false; 561 } 562 563 for (ParcelUuid parcelSolicitationUuid : solicitationUuids) { 564 UUID solicitationUuidMask = 565 parcelSolicitationUuidMask == null 566 ? null 567 : parcelSolicitationUuidMask.getUuid(); 568 if (matchesServiceUuid( 569 solicitationUuid.getUuid(), 570 solicitationUuidMask, 571 parcelSolicitationUuid.getUuid())) { 572 return true; 573 } 574 } 575 return false; 576 } 577 578 // Check whether the data pattern matches the parsed data. matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData)579 static boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { 580 if (parsedData == null || parsedData.length < data.length) { 581 return false; 582 } 583 if (dataMask == null) { 584 for (int i = 0; i < data.length; ++i) { 585 if (parsedData[i] != data[i]) { 586 return false; 587 } 588 } 589 return true; 590 } 591 for (int i = 0; i < data.length; ++i) { 592 if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { 593 return false; 594 } 595 } 596 return true; 597 } 598 599 @Override toString()600 public String toString() { 601 return "BluetoothLeScanFilter [mDeviceName=" 602 + mDeviceName 603 + ", mDeviceAddress=" 604 + mDeviceAddress 605 + ", mUuid=" 606 + mServiceUuid 607 + ", mUuidMask=" 608 + mServiceUuidMask 609 + ", mServiceSolicitationUuid=" 610 + mServiceSolicitationUuid 611 + ", mServiceSolicitationUuidMask=" 612 + mServiceSolicitationUuidMask 613 + ", mServiceDataUuid=" 614 + Objects.toString(mServiceDataUuid) 615 + ", mServiceData=" 616 + Arrays.toString(mServiceData) 617 + ", mServiceDataMask=" 618 + Arrays.toString(mServiceDataMask) 619 + ", mManufacturerId=" 620 + mManufacturerId 621 + ", mManufacturerData=" 622 + Arrays.toString(mManufacturerData) 623 + ", mManufacturerDataMask=" 624 + Arrays.toString(mManufacturerDataMask) 625 + ", mAdvertisingDataType=" 626 + mAdvertisingDataType 627 + ", mAdvertisingData=" 628 + Arrays.toString(mAdvertisingData) 629 + ", mAdvertisingDataMask=" 630 + Arrays.toString(mAdvertisingDataMask) 631 + ", mTransportBlockFilter=" 632 + mTransportBlockFilter 633 + "]"; 634 } 635 636 @Override hashCode()637 public int hashCode() { 638 return Objects.hash( 639 mDeviceName, 640 mDeviceAddress, 641 mManufacturerId, 642 Arrays.hashCode(mManufacturerData), 643 Arrays.hashCode(mManufacturerDataMask), 644 mServiceDataUuid, 645 Arrays.hashCode(mServiceData), 646 Arrays.hashCode(mServiceDataMask), 647 mServiceUuid, 648 mServiceUuidMask, 649 mServiceSolicitationUuid, 650 mServiceSolicitationUuidMask, 651 mAdvertisingDataType, 652 Arrays.hashCode(mAdvertisingData), 653 Arrays.hashCode(mAdvertisingDataMask), 654 mTransportBlockFilter); 655 } 656 657 @Override equals(@ullable Object obj)658 public boolean equals(@Nullable Object obj) { 659 if (this == obj) { 660 return true; 661 } 662 if (obj == null || getClass() != obj.getClass()) { 663 return false; 664 } 665 ScanFilter other = (ScanFilter) obj; 666 return Objects.equals(mDeviceName, other.mDeviceName) 667 && Objects.equals(mDeviceAddress, other.mDeviceAddress) 668 && mManufacturerId == other.mManufacturerId 669 && Objects.deepEquals(mManufacturerData, other.mManufacturerData) 670 && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) 671 && Objects.equals(mServiceDataUuid, other.mServiceDataUuid) 672 && Objects.deepEquals(mServiceData, other.mServiceData) 673 && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) 674 && Objects.equals(mServiceUuid, other.mServiceUuid) 675 && Objects.equals(mServiceUuidMask, other.mServiceUuidMask) 676 && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid) 677 && Objects.equals(mServiceSolicitationUuidMask, other.mServiceSolicitationUuidMask) 678 && mAdvertisingDataType == other.mAdvertisingDataType 679 && Objects.deepEquals(mAdvertisingData, other.mAdvertisingData) 680 && Objects.deepEquals(mAdvertisingDataMask, other.mAdvertisingDataMask) 681 && Objects.equals(mTransportBlockFilter, other.getTransportBlockFilter()); 682 } 683 684 /** 685 * Checks if the scanfilter is empty 686 * 687 * @hide 688 */ isAllFieldsEmpty()689 public boolean isAllFieldsEmpty() { 690 return EMPTY.equals(this); 691 } 692 693 /** Builder class for {@link ScanFilter}. */ 694 public static final class Builder { 695 696 /** @hide */ 697 @SystemApi public static final int LEN_IRK_OCTETS = 16; 698 699 private String mDeviceName; 700 private String mDeviceAddress; 701 private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC; 702 private byte[] mIrk; 703 704 private ParcelUuid mServiceUuid; 705 private ParcelUuid mUuidMask; 706 707 private ParcelUuid mServiceSolicitationUuid; 708 private ParcelUuid mServiceSolicitationUuidMask; 709 710 private ParcelUuid mServiceDataUuid; 711 private byte[] mServiceData; 712 private byte[] mServiceDataMask; 713 714 private int mManufacturerId = -1; 715 private byte[] mManufacturerData; 716 private byte[] mManufacturerDataMask; 717 718 private int mAdvertisingDataType = ScanRecord.DATA_TYPE_NONE; 719 private byte[] mAdvertisingData; 720 private byte[] mAdvertisingDataMask; 721 722 private TransportBlockFilter mTransportBlockFilter = null; 723 724 /** Set filter on device name. */ setDeviceName(String deviceName)725 public Builder setDeviceName(String deviceName) { 726 mDeviceName = deviceName; 727 return this; 728 } 729 730 /** 731 * Set a scan filter on the remote device address. 732 * 733 * <p>The address passed to this API must be in big endian byte order. It needs to be in the 734 * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link 735 * BluetoothAdapter#checkBluetoothAddress}. The @AddressType is defaulted to {@link 736 * BluetoothDevice#ADDRESS_TYPE_PUBLIC}. 737 * 738 * @param deviceAddress the remote device Bluetooth address for the filter 739 * @throws IllegalArgumentException if the {@code deviceAddress} is invalid 740 */ setDeviceAddress(String deviceAddress)741 public Builder setDeviceAddress(String deviceAddress) { 742 if (deviceAddress == null) { 743 mDeviceAddress = deviceAddress; 744 return this; 745 } 746 return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); 747 } 748 749 /** 750 * Set a scan filter on the remote device address with an address type. 751 * 752 * <p>The address passed to this API must be in big endian byte order. It needs to be in the 753 * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link 754 * BluetoothAdapter#checkBluetoothAddress}. 755 * 756 * @param deviceAddress the remote device Bluetooth address for the filter 757 * @param addressType indication of the type of address 758 * @throws IllegalArgumentException If the {@code deviceAddress} is invalid 759 * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not 760 * either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} or {@link 761 * BluetoothDevice#ADDRESS_TYPE_RANDOM} 762 * @throws NullPointerException if {@code deviceAddress} is null 763 * @hide 764 */ 765 @NonNull 766 @SystemApi setDeviceAddress( @onNull String deviceAddress, @AddressType int addressType)767 public Builder setDeviceAddress( 768 @NonNull String deviceAddress, @AddressType int addressType) { 769 return setDeviceAddressInternal(deviceAddress, addressType, null); 770 } 771 772 /** 773 * Set a scan filter on the remote device address with an address type and the Identity 774 * Resolving Key (IRK). 775 * 776 * <p>The address passed to this API must be either a public or random static address in big 777 * endian byte order. It needs to be in the format of "01:02:03:AB:CD:EF". The device 778 * address can be validated using {@link BluetoothAdapter#checkBluetoothAddress}. 779 * 780 * <p>The IRK is used to resolve a static address from a private address. The IRK must be 781 * provided in little endian byte order. 782 * 783 * <p>When using this API, it is recommended to continue scanning until the device is 784 * bonded. 785 * 786 * <p>The resulting {@link ScanResult} that matches this filter will contain an {@link 787 * BluetoothDevice} object for which the {@link BluetoothDevice#getAddress} method will 788 * return the device address passed as a parameter in this method. 789 * 790 * <p>It is not recommended to use this API for discovering devices that are already bonded, 791 * but note if the device with this IRK is already bonded, calling {@link 792 * BluetoothDevice#getAddress} on the {@link ScanResult} using this filter will return the 793 * device address that was used to initiate bonding, and may not match the address passed 794 * into this method in that scenario. 795 * 796 * @param deviceAddress the remote device Bluetooth address for the filter in big endian 797 * order 798 * @param addressType indication of the type of address 799 * @param irk non-null little endian byte array representing the Identity Resolving Key 800 * @throws IllegalArgumentException If the {@code deviceAddress} is invalid 801 * @throws IllegalArgumentException if the {@code irk} is invalid length 802 * @throws IllegalArgumentException If the {@code addressType} is an invalid length or is 803 * not PUBLIC or RANDOM STATIC 804 * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null 805 * @hide 806 */ 807 @NonNull 808 @SystemApi setDeviceAddress( @onNull String deviceAddress, @AddressType int addressType, @NonNull byte[] irk)809 public Builder setDeviceAddress( 810 @NonNull String deviceAddress, @AddressType int addressType, @NonNull byte[] irk) { 811 requireNonNull(irk); 812 if (irk.length != LEN_IRK_OCTETS) { 813 throw new IllegalArgumentException("'irk' is invalid length!"); 814 } 815 return setDeviceAddressInternal(deviceAddress, addressType, irk); 816 } 817 818 /** 819 * Set filter on Address with AddressType and the Identity Resolving Key (IRK). 820 * 821 * <p>Internal setter for the device address 822 * 823 * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the 824 * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link 825 * BluetoothAdapter#checkBluetoothAddress}. 826 * @param addressType indication of the type of address 827 * @param irk non-null little endian byte array representing the Identity Resolving Key; 828 * nullable internally. 829 * @throws IllegalArgumentException if the {@code deviceAddress} is invalid 830 * @throws IllegalArgumentException if the {@code addressType} is not PUBLIC or RANDOM 831 * STATIC when an IRK is present 832 * @throws NullPointerException if {@code deviceAddress} is null 833 * @hide 834 */ 835 @NonNull setDeviceAddressInternal( @onNull String deviceAddress, @AddressType int addressType, @Nullable byte[] irk)836 private Builder setDeviceAddressInternal( 837 @NonNull String deviceAddress, @AddressType int addressType, @Nullable byte[] irk) { 838 839 // Make sure our deviceAddress is valid! 840 requireNonNull(deviceAddress); 841 if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { 842 throw new IllegalArgumentException("invalid device address " + deviceAddress); 843 } 844 845 // Verify type range 846 if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC 847 || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) { 848 throw new IllegalArgumentException("'addressType' is invalid!"); 849 } 850 851 // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address. 852 if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) { 853 // Don't want a bad combination of address and irk! 854 if (irk != null) { 855 // Since there are 3 possible RANDOM subtypes we must check to make sure 856 // the correct type of address is used. 857 if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) { 858 throw new IllegalArgumentException( 859 "Invalid combination: IRK requires either a PUBLIC or " 860 + "RANDOM (STATIC) Address"); 861 } 862 } 863 } 864 865 // PUBLIC doesn't require extra work 866 // Without an IRK any address may be accepted 867 868 mDeviceAddress = deviceAddress; 869 mAddressType = addressType; 870 mIrk = irk; 871 return this; 872 } 873 874 /** Set filter on service uuid. */ setServiceUuid(ParcelUuid serviceUuid)875 public Builder setServiceUuid(ParcelUuid serviceUuid) { 876 mServiceUuid = serviceUuid; 877 mUuidMask = null; // clear uuid mask 878 return this; 879 } 880 881 /** 882 * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the {@code 883 * serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the bit in 884 * {@code serviceUuid}, and 0 to ignore that bit. 885 * 886 * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code 887 * uuidMask} is not {@code null}. 888 */ setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask)889 public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) { 890 if (mUuidMask != null && mServiceUuid == null) { 891 throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); 892 } 893 mServiceUuid = serviceUuid; 894 mUuidMask = uuidMask; 895 return this; 896 } 897 898 /** Set filter on service solicitation uuid. */ setServiceSolicitationUuid( @ullable ParcelUuid serviceSolicitationUuid)899 public @NonNull Builder setServiceSolicitationUuid( 900 @Nullable ParcelUuid serviceSolicitationUuid) { 901 mServiceSolicitationUuid = serviceSolicitationUuid; 902 if (serviceSolicitationUuid == null) { 903 mServiceSolicitationUuidMask = null; 904 } 905 return this; 906 } 907 908 /** 909 * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the 910 * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to 911 * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to 912 * ignore that bit. 913 * 914 * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null. 915 * @param solicitationUuidMask can be null or a mask with no restriction. 916 * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but 917 * {@code solicitationUuidMask} is not {@code null}. 918 */ setServiceSolicitationUuid( @ullable ParcelUuid serviceSolicitationUuid, @Nullable ParcelUuid solicitationUuidMask)919 public @NonNull Builder setServiceSolicitationUuid( 920 @Nullable ParcelUuid serviceSolicitationUuid, 921 @Nullable ParcelUuid solicitationUuidMask) { 922 if (solicitationUuidMask != null && serviceSolicitationUuid == null) { 923 throw new IllegalArgumentException( 924 "SolicitationUuid is null while SolicitationUuidMask is not null!"); 925 } 926 mServiceSolicitationUuid = serviceSolicitationUuid; 927 mServiceSolicitationUuidMask = solicitationUuidMask; 928 return this; 929 } 930 931 /** 932 * Set filtering on service data. 933 * 934 * @throws IllegalArgumentException If {@code serviceDataUuid} is null. 935 */ setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData)936 public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { 937 if (serviceDataUuid == null) { 938 throw new IllegalArgumentException("serviceDataUuid is null"); 939 } 940 mServiceDataUuid = serviceDataUuid; 941 mServiceData = serviceData; 942 mServiceDataMask = null; // clear service data mask 943 return this; 944 } 945 946 /** 947 * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to 948 * match the one in service data, otherwise set it to 0 to ignore that bit. 949 * 950 * <p>The {@code serviceDataMask} must have the same length of the {@code serviceData}. 951 * 952 * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code 953 * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code 954 * serviceDataMask} and {@code serviceData} has different length. 955 */ setServiceData( ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask)956 public Builder setServiceData( 957 ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask) { 958 if (serviceDataUuid == null) { 959 throw new IllegalArgumentException("serviceDataUuid is null"); 960 } 961 if (mServiceDataMask != null) { 962 if (mServiceData == null) { 963 throw new IllegalArgumentException( 964 "serviceData is null while serviceDataMask is not null"); 965 } 966 // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two 967 // byte array need to be the same. 968 if (mServiceData.length != mServiceDataMask.length) { 969 throw new IllegalArgumentException( 970 "size mismatch for service data and service data mask"); 971 } 972 } 973 mServiceDataUuid = serviceDataUuid; 974 mServiceData = serviceData; 975 mServiceDataMask = serviceDataMask; 976 return this; 977 } 978 979 /** 980 * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. 981 * 982 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. 983 */ setManufacturerData(int manufacturerId, byte[] manufacturerData)984 public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) { 985 if (manufacturerData != null && manufacturerId < 0) { 986 throw new IllegalArgumentException("invalid manufacture id"); 987 } 988 mManufacturerId = manufacturerId; 989 mManufacturerData = manufacturerData; 990 mManufacturerDataMask = null; // clear manufacturer data mask 991 return this; 992 } 993 994 /** 995 * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs 996 * to match the one in manufacturer data, otherwise set it to 0. 997 * 998 * <p>The {@code manufacturerDataMask} must have the same length of {@code 999 * manufacturerData}. 1000 * 1001 * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code 1002 * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code 1003 * manufacturerData} and {@code manufacturerDataMask} have different length. 1004 */ setManufacturerData( int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask)1005 public Builder setManufacturerData( 1006 int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { 1007 if (manufacturerData != null && manufacturerId < 0) { 1008 throw new IllegalArgumentException("invalid manufacture id"); 1009 } 1010 if (mManufacturerDataMask != null) { 1011 if (mManufacturerData == null) { 1012 throw new IllegalArgumentException( 1013 "manufacturerData is null while manufacturerDataMask is not null"); 1014 } 1015 // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths 1016 // of the two byte array need to be the same. 1017 if (mManufacturerData.length != mManufacturerDataMask.length) { 1018 throw new IllegalArgumentException( 1019 "size mismatch for manufacturerData and manufacturerDataMask"); 1020 } 1021 } 1022 mManufacturerId = manufacturerId; 1023 mManufacturerData = manufacturerData; 1024 mManufacturerDataMask = manufacturerDataMask; 1025 return this; 1026 } 1027 1028 /** 1029 * Set filter information for a transport block in Transport Discovery Service advertisement 1030 * 1031 * <p>Use {@link BluetoothAdapter#getOffloadedTransportDiscoveryDataScanSupported()} to 1032 * check whether transport discovery data filtering is supported on this device before 1033 * calling this method. 1034 * 1035 * @param transportBlockFilter filter data for a transport block in Transport Discovery 1036 * Service advertisement 1037 * @throws IllegalArgumentException if Transport Discovery Data filter is not supported. 1038 * @return this builder 1039 * @hide 1040 */ 1041 @SystemApi 1042 @RequiresBluetoothScanPermission 1043 @RequiresPermission( 1044 allOf = { 1045 android.Manifest.permission.BLUETOOTH_SCAN, 1046 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1047 }) 1048 @NonNull setTransportBlockFilter(@onNull TransportBlockFilter transportBlockFilter)1049 public Builder setTransportBlockFilter(@NonNull TransportBlockFilter transportBlockFilter) { 1050 BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 1051 1052 if (bluetoothAdapter == null) { 1053 throw new IllegalArgumentException("BluetoothAdapter is null"); 1054 } 1055 if (bluetoothAdapter.getOffloadedTransportDiscoveryDataScanSupported() 1056 != BluetoothStatusCodes.FEATURE_SUPPORTED) { 1057 throw new IllegalArgumentException( 1058 "Transport Discovery Data filter is not supported"); 1059 } 1060 1061 mTransportBlockFilter = transportBlockFilter; 1062 return this; 1063 } 1064 1065 /** 1066 * Set filter on advertising data with specific advertising data type. For any bit in the 1067 * mask, set it the 1 if it needs to match the one in advertising data, otherwise set it to 1068 * 0. 1069 * 1070 * <p>The values of {@code advertisingDataType} are assigned by Bluetooth SIG. For more 1071 * details refer to Bluetooth Generic Access Profile. 1072 * (https://www.bluetooth.com/specifications/assigned-numbers/) The {@code 1073 * advertisingDataMask} must have the same length of {@code advertisingData}. 1074 * 1075 * @throws IllegalArgumentException If the {@code advertisingDataType} is invalid, {@code 1076 * advertisingData} or {@code advertisingDataMask} is null or {@code advertisingData} 1077 * and {@code advertisingDataMask} have different length. 1078 */ setAdvertisingDataTypeWithData( @dvertisingDataType int advertisingDataType, @NonNull byte[] advertisingData, @NonNull byte[] advertisingDataMask)1079 public @NonNull Builder setAdvertisingDataTypeWithData( 1080 @AdvertisingDataType int advertisingDataType, 1081 @NonNull byte[] advertisingData, 1082 @NonNull byte[] advertisingDataMask) { 1083 if (advertisingDataType < 0) { 1084 throw new IllegalArgumentException("invalid advertising data type"); 1085 } 1086 if (mAdvertisingDataMask != null) { 1087 if (mAdvertisingData == null) { 1088 throw new IllegalArgumentException( 1089 "mAdvertisingData is null while mAdvertisingDataMask is not null"); 1090 } 1091 // Since the mAdvertisingDataMask is a bit mask for mAdvertisingData, the lengths 1092 // of the two byte array need to be the same. 1093 if (mAdvertisingData.length != mAdvertisingDataMask.length) { 1094 throw new IllegalArgumentException( 1095 "size mismatch for mAdvertisingData and mAdvertisingDataMask"); 1096 } 1097 } 1098 mAdvertisingDataType = advertisingDataType; 1099 mAdvertisingData = advertisingData; 1100 mAdvertisingDataMask = advertisingDataMask; 1101 return this; 1102 } 1103 1104 /** 1105 * Set filter on advertising data with specific advertising data type. 1106 * 1107 * <p>The values of {@code advertisingDataType} are assigned by Bluetooth SIG. For more 1108 * details refer to Bluetooth Generic Access Profile. 1109 * (https://www.bluetooth.com/specifications/assigned-numbers/) 1110 * 1111 * @throws IllegalArgumentException If the {@code advertisingDataType} is invalid 1112 */ setAdvertisingDataType( @dvertisingDataType int advertisingDataType)1113 public @NonNull Builder setAdvertisingDataType( 1114 @AdvertisingDataType int advertisingDataType) { 1115 if (advertisingDataType < 0) { 1116 throw new IllegalArgumentException("invalid advertising data type"); 1117 } 1118 mAdvertisingDataType = advertisingDataType; 1119 return this; 1120 } 1121 1122 /** 1123 * Build {@link ScanFilter}. 1124 * 1125 * @throws IllegalArgumentException If the filter cannot be built. 1126 */ build()1127 public ScanFilter build() { 1128 return new ScanFilter( 1129 mDeviceName, 1130 mDeviceAddress, 1131 mServiceUuid, 1132 mUuidMask, 1133 mServiceSolicitationUuid, 1134 mServiceSolicitationUuidMask, 1135 mServiceDataUuid, 1136 mServiceData, 1137 mServiceDataMask, 1138 mManufacturerId, 1139 mManufacturerData, 1140 mManufacturerDataMask, 1141 mAddressType, 1142 mIrk, 1143 mAdvertisingDataType, 1144 mAdvertisingData, 1145 mAdvertisingDataMask, 1146 mTransportBlockFilter); 1147 } 1148 } 1149 } 1150