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 com.android.server.nearby.common.ble; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.fail; 22 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothAssignedNumbers; 25 import android.bluetooth.BluetoothDevice; 26 import android.bluetooth.le.ScanFilter; 27 import android.os.Parcel; 28 import android.os.ParcelUuid; 29 import android.util.SparseArray; 30 31 import androidx.test.ext.junit.runners.AndroidJUnit4; 32 33 import com.android.server.nearby.common.ble.testing.FastPairTestData; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.nio.ByteBuffer; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.UUID; 43 44 @RunWith(AndroidJUnit4.class) 45 public class BleFilterTest { 46 47 48 public static final ParcelUuid EDDYSTONE_SERVICE_DATA_PARCELUUID = 49 ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"); 50 51 private final BleFilter mEddystoneFilter = createEddystoneFilter(); 52 private final BleFilter mEddystoneUidFilter = createEddystoneUidFilter(); 53 private final BleFilter mEddystoneUrlFilter = createEddystoneUrlFilter(); 54 private final BleFilter mEddystoneEidFilter = createEddystoneEidFilter(); 55 private final BleFilter mIBeaconWithoutUuidFilter = createIBeaconWithoutUuidFilter(); 56 private final BleFilter mIBeaconWithUuidFilter = createIBeaconWithUuidFilter(); 57 private final BleFilter mChromecastFilter = 58 new BleFilter.Builder().setServiceUuid( 59 new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB"))) 60 .build(); 61 private final BleFilter mEddystoneWithDeviceNameFilter = 62 new BleFilter.Builder() 63 .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID) 64 .setDeviceName("BERT") 65 .build(); 66 private final BleFilter mEddystoneWithDeviceAddressFilter = 67 new BleFilter.Builder() 68 .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID) 69 .setDeviceAddress("00:11:22:33:AA:BB") 70 .build(); 71 private final BleFilter mServiceUuidWithMaskFilter1 = 72 new BleFilter.Builder() 73 .setServiceUuid( 74 new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")), 75 new ParcelUuid(UUID.fromString("0000000-0000-000-FFFF-FFFFFFFFFFFF"))) 76 .build(); 77 private final BleFilter mServiceUuidWithMaskFilter2 = 78 new BleFilter.Builder() 79 .setServiceUuid( 80 new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")), 81 new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF"))) 82 .build(); 83 84 private final BleFilter mSmartSetupFilter = 85 new BleFilter.Builder() 86 .setManufacturerData( 87 BluetoothAssignedNumbers.GOOGLE, 88 new byte[] {0x00, 0x10}, 89 new byte[] {0x00, (byte) 0xFF}) 90 .build(); 91 private final BleFilter mWearFilter = 92 new BleFilter.Builder() 93 .setManufacturerData( 94 BluetoothAssignedNumbers.GOOGLE, 95 new byte[] {0x00, 0x00, 0x00}, 96 new byte[] {0x00, 0x00, (byte) 0xFF}) 97 .build(); 98 private final BleFilter mFakeSmartSetupSubsetFilter = 99 new BleFilter.Builder() 100 .setManufacturerData( 101 BluetoothAssignedNumbers.GOOGLE, 102 new byte[] {0x00, 0x10, 0x50}, 103 new byte[] {0x00, (byte) 0xFF, (byte) 0xFF}) 104 .build(); 105 private final BleFilter mFakeSmartSetupNotSubsetFilter = 106 new BleFilter.Builder() 107 .setManufacturerData( 108 BluetoothAssignedNumbers.GOOGLE, 109 new byte[] {0x00, 0x10, 0x50}, 110 new byte[] {0x00, (byte) 0x00, (byte) 0xFF}) 111 .build(); 112 113 private final BleFilter mFakeFilter1 = 114 new BleFilter.Builder() 115 .setServiceData( 116 ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), 117 new byte[] {0x51, 0x64}, 118 new byte[] {0x00, (byte) 0xFF}) 119 .build(); 120 private final BleFilter mFakeFilter2 = 121 new BleFilter.Builder() 122 .setServiceData( 123 ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), 124 new byte[] {0x51, 0x64, 0x34}, 125 new byte[] {0x00, (byte) 0xFF, (byte) 0xFF}) 126 .build(); 127 128 private ParcelUuid mServiceDataUuid; 129 private BleSighting mBleSighting; 130 private BleFilter.Builder mFilterBuilder; 131 132 @Before setUp()133 public void setUp() throws Exception { 134 // This is the service data UUID in TestData.sd1. 135 // Can't be static because of Robolectric. 136 mServiceDataUuid = ParcelUuid.fromString("000000E0-0000-1000-8000-00805F9B34FB"); 137 138 byte[] bleRecordBytes = 139 new byte[]{ 140 0x02, 141 0x01, 142 0x1a, // advertising flags 143 0x05, 144 0x02, 145 0x0b, 146 0x11, 147 0x0a, 148 0x11, // 16 bit service uuids 149 0x04, 150 0x09, 151 0x50, 152 0x65, 153 0x64, // setName 154 0x02, 155 0x0A, 156 (byte) 0xec, // tx power level 157 0x05, 158 0x16, 159 0x0b, 160 0x11, 161 0x50, 162 0x64, // service data 163 0x05, 164 (byte) 0xff, 165 (byte) 0xe0, 166 0x00, 167 0x02, 168 0x15, // manufacturer specific data 169 0x03, 170 0x50, 171 0x01, 172 0x02, // an unknown data type won't cause trouble 173 }; 174 175 mBleSighting = new BleSighting(null /* device */, bleRecordBytes, 176 -10, 1397545200000000L); 177 mFilterBuilder = new BleFilter.Builder(); 178 } 179 180 @Test setNameFilter()181 public void setNameFilter() { 182 BleFilter filter = mFilterBuilder.setDeviceName("Ped").build(); 183 assertThat(filter.matches(mBleSighting)).isTrue(); 184 185 filter = mFilterBuilder.setDeviceName("Pem").build(); 186 assertThat(filter.matches(mBleSighting)).isFalse(); 187 } 188 189 @Test setServiceUuidFilter()190 public void setServiceUuidFilter() { 191 BleFilter filter = 192 mFilterBuilder.setServiceUuid( 193 ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")) 194 .build(); 195 assertThat(filter.matches(mBleSighting)).isTrue(); 196 197 filter = 198 mFilterBuilder.setServiceUuid( 199 ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")) 200 .build(); 201 assertThat(filter.matches(mBleSighting)).isFalse(); 202 203 filter = 204 mFilterBuilder 205 .setServiceUuid( 206 ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"), 207 ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) 208 .build(); 209 assertThat(filter.matches(mBleSighting)).isTrue(); 210 } 211 212 @Test setServiceDataFilter()213 public void setServiceDataFilter() { 214 byte[] setServiceData = new byte[]{0x50, 0x64}; 215 ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); 216 BleFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); 217 assertThat(filter.matches(mBleSighting)).isTrue(); 218 219 byte[] emptyData = new byte[0]; 220 filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build(); 221 assertThat(filter.matches(mBleSighting)).isTrue(); 222 223 byte[] prefixData = new byte[]{0x50}; 224 filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build(); 225 assertThat(filter.matches(mBleSighting)).isTrue(); 226 227 byte[] nonMatchData = new byte[]{0x51, 0x64}; 228 byte[] mask = new byte[]{(byte) 0x00, (byte) 0xFF}; 229 filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build(); 230 assertThat(filter.matches(mBleSighting)).isTrue(); 231 232 filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build(); 233 assertThat(filter.matches(mBleSighting)).isFalse(); 234 } 235 236 @Test manufacturerSpecificData()237 public void manufacturerSpecificData() { 238 byte[] setManufacturerData = new byte[]{0x02, 0x15}; 239 int manufacturerId = 0xE0; 240 BleFilter filter = 241 mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build(); 242 assertThat(filter.matches(mBleSighting)).isTrue(); 243 244 byte[] emptyData = new byte[0]; 245 filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build(); 246 assertThat(filter.matches(mBleSighting)).isTrue(); 247 248 byte[] prefixData = new byte[]{0x02}; 249 filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build(); 250 assertThat(filter.matches(mBleSighting)).isTrue(); 251 252 // Data and mask are nullable. Check that we still match when they're null. 253 filter = mFilterBuilder.setManufacturerData(manufacturerId, 254 null /* data */).build(); 255 assertThat(filter.matches(mBleSighting)).isTrue(); 256 filter = mFilterBuilder.setManufacturerData(manufacturerId, 257 null /* data */, null /* mask */).build(); 258 assertThat(filter.matches(mBleSighting)).isTrue(); 259 260 // Test data mask 261 byte[] nonMatchData = new byte[]{0x02, 0x14}; 262 filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build(); 263 assertThat(filter.matches(mBleSighting)).isFalse(); 264 byte[] mask = new byte[]{(byte) 0xFF, (byte) 0x00}; 265 filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build(); 266 assertThat(filter.matches(mBleSighting)).isTrue(); 267 } 268 269 @Test manufacturerDataNotInBleRecord()270 public void manufacturerDataNotInBleRecord() { 271 byte[] bleRecord = FastPairTestData.adv_2; 272 // Verify manufacturer with no data 273 byte[] data = {(byte) 0xe0, (byte) 0x00}; 274 BleFilter filter = mFilterBuilder.setManufacturerData(0x00e0, data).build(); 275 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 276 } 277 278 @Test manufacturerDataMaskNotInBleRecord()279 public void manufacturerDataMaskNotInBleRecord() { 280 byte[] bleRecord = FastPairTestData.adv_2; 281 282 // Verify matching partial manufacturer with data and mask 283 byte[] data = {(byte) 0x15}; 284 byte[] mask = {(byte) 0xff}; 285 286 BleFilter filter = mFilterBuilder 287 .setManufacturerData(0x00e0, data, mask).build(); 288 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 289 } 290 291 292 @Test serviceData()293 public void serviceData() throws Exception { 294 byte[] bleRecord = FastPairTestData.sd1; 295 byte[] serviceData = {(byte) 0x15}; 296 297 // Verify manufacturer 2-byte UUID with no data 298 BleFilter filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData).build(); 299 assertMatches(filter, null, 0, bleRecord); 300 } 301 302 @Test serviceDataNoMatch()303 public void serviceDataNoMatch() { 304 byte[] bleRecord = FastPairTestData.sd1; 305 byte[] serviceData = {(byte) 0xe1, (byte) 0x00}; 306 307 // Verify manufacturer 2-byte UUID with no data 308 BleFilter filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData).build(); 309 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 310 } 311 312 @Test serviceDataUuidNotInBleRecord()313 public void serviceDataUuidNotInBleRecord() { 314 byte[] bleRecord = FastPairTestData.eir_1; 315 byte[] serviceData = {(byte) 0xe0, (byte) 0x00}; 316 317 // Verify Service Data with 2-byte UUID, no data, and NOT in scan record 318 BleFilter filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData).build(); 319 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 320 } 321 322 @Test serviceDataMask()323 public void serviceDataMask() { 324 byte[] bleRecord = FastPairTestData.sd1; 325 BleFilter filter; 326 327 // Verify matching partial manufacturer with data and mask 328 byte[] serviceData1 = {(byte) 0x15}; 329 byte[] mask1 = {(byte) 0xff}; 330 filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData1, mask1).build(); 331 assertMatches(filter, null, 0, bleRecord); 332 } 333 334 @Test serviceDataMaskNoMatch()335 public void serviceDataMaskNoMatch() { 336 byte[] bleRecord = FastPairTestData.sd1; 337 BleFilter filter; 338 339 // Verify non-matching partial manufacturer with data and mask 340 byte[] serviceData2 = {(byte) 0xe0, (byte) 0x00, (byte) 0x10}; 341 byte[] mask2 = {(byte) 0xff, (byte) 0xff, (byte) 0xff}; 342 filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData2, mask2).build(); 343 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 344 } 345 346 @Test(expected = IllegalArgumentException.class) serviceDataMaskWithDifferentLength()347 public void serviceDataMaskWithDifferentLength() { 348 // Different lengths for data and mask. 349 byte[] serviceData = {(byte) 0xe0, (byte) 0x00, (byte) 0x10}; 350 byte[] mask = {(byte) 0xff, (byte) 0xff}; 351 352 //expected.expect(IllegalArgumentException.class); 353 354 mFilterBuilder.setServiceData(mServiceDataUuid, serviceData, mask).build(); 355 } 356 357 @Test serviceDataMaskNotInBleRecord()358 public void serviceDataMaskNotInBleRecord() { 359 byte[] bleRecord = FastPairTestData.eir_1; 360 BleFilter filter; 361 362 // Verify matching partial manufacturer with data and mask 363 byte[] serviceData1 = {(byte) 0xe0, (byte) 0x00, (byte) 0x15}; 364 byte[] mask1 = {(byte) 0xff, (byte) 0xff, (byte) 0xff}; 365 filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData1, mask1).build(); 366 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 367 } 368 369 370 @Test deviceNameTest()371 public void deviceNameTest() { 372 // Verify the name filter matches 373 byte[] bleRecord = FastPairTestData.adv_1; 374 BleFilter filter = mFilterBuilder.setDeviceName("Pedometer").build(); 375 assertMatches(filter, null, 0, bleRecord); 376 } 377 378 @Test deviceNameNoMatch()379 public void deviceNameNoMatch() { 380 // Verify the name filter does not match 381 byte[] bleRecord = FastPairTestData.adv_1; 382 BleFilter filter = mFilterBuilder.setDeviceName("Foo").build(); 383 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 384 } 385 386 @Test deviceNameNotInBleRecord()387 public void deviceNameNotInBleRecord() { 388 // Verify the name filter does not match 389 byte[] bleRecord = FastPairTestData.eir_1; 390 BleFilter filter = mFilterBuilder.setDeviceName("Pedometer").build(); 391 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 392 } 393 394 @Test serviceUuid()395 public void serviceUuid() { 396 byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid; 397 ParcelUuid uuid = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"); 398 399 BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build(); 400 assertMatches(filter, null, 0, bleRecord); 401 } 402 403 @Test serviceUuidNoMatch()404 public void serviceUuidNoMatch() { 405 // Verify the name filter does not match 406 byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid; 407 ParcelUuid uuid = ParcelUuid.fromString("00001804-0000-1000-8000-000000000000"); 408 409 BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build(); 410 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 411 } 412 413 @Test serviceUuidNotInBleRecord()414 public void serviceUuidNotInBleRecord() { 415 // Verify the name filter does not match 416 byte[] bleRecord = FastPairTestData.eir_1; 417 ParcelUuid uuid = ParcelUuid.fromString("00001804-0000-1000-8000-000000000000"); 418 419 BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build(); 420 assertThat(matches(filter, null, 0, bleRecord)).isFalse(); 421 } 422 423 @Test serviceUuidMask()424 public void serviceUuidMask() { 425 byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid; 426 ParcelUuid uuid = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"); 427 ParcelUuid mask = ParcelUuid.fromString("00000000-0000-0000-0000-FFFFFFFFFFFF"); 428 BleFilter filter = mFilterBuilder.setServiceUuid(uuid, mask).build(); 429 assertMatches(filter, null, 0, bleRecord); 430 } 431 432 433 @Test macAddress()434 public void macAddress() { 435 byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid; 436 String macAddress = "00:11:22:33:AA:BB"; 437 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 438 439 BluetoothDevice device = adapter.getRemoteDevice(macAddress); 440 BleFilter filter = mFilterBuilder.setDeviceAddress(macAddress).build(); 441 assertMatches(filter, device, 0, bleRecord); 442 } 443 444 @Test macAddressNoMatch()445 public void macAddressNoMatch() { 446 byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid; 447 String macAddress = "00:11:22:33:AA:00"; 448 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 449 450 BluetoothDevice device = adapter.getRemoteDevice("00:11:22:33:AA:BB"); 451 BleFilter filter = mFilterBuilder.setDeviceAddress(macAddress).build(); 452 assertThat(matches(filter, device, 0, bleRecord)).isFalse(); 453 } 454 455 @Test eddystoneIsSuperset()456 public void eddystoneIsSuperset() { 457 // Verify eddystone subtypes pass. 458 assertThat(mEddystoneFilter.isSuperset(mEddystoneFilter)).isTrue(); 459 assertThat(mEddystoneUidFilter.isSuperset(mEddystoneUidFilter)).isTrue(); 460 assertThat(mEddystoneFilter.isSuperset(mEddystoneUidFilter)).isTrue(); 461 assertThat(mEddystoneFilter.isSuperset(mEddystoneEidFilter)).isTrue(); 462 assertThat(mEddystoneFilter.isSuperset(mEddystoneUrlFilter)).isTrue(); 463 464 // Non-eddystone beacon filters should never be supersets. 465 assertThat(mEddystoneFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse(); 466 assertThat(mEddystoneFilter.isSuperset(mWearFilter)).isFalse(); 467 assertThat(mEddystoneFilter.isSuperset(mSmartSetupFilter)).isFalse(); 468 assertThat(mEddystoneFilter.isSuperset(mChromecastFilter)).isFalse(); 469 assertThat(mEddystoneFilter.isSuperset(mFakeFilter1)).isFalse(); 470 assertThat(mEddystoneFilter.isSuperset(mFakeFilter2)).isFalse(); 471 472 assertThat(mEddystoneUidFilter.isSuperset(mWearFilter)).isFalse(); 473 assertThat(mEddystoneUidFilter.isSuperset(mSmartSetupFilter)).isFalse(); 474 assertThat(mEddystoneUidFilter.isSuperset(mChromecastFilter)).isFalse(); 475 assertThat(mEddystoneUidFilter.isSuperset(mFakeFilter1)).isFalse(); 476 assertThat(mEddystoneUidFilter.isSuperset(mFakeFilter2)).isFalse(); 477 } 478 479 @Test iBeaconIsSuperset()480 public void iBeaconIsSuperset() { 481 // Verify that an iBeacon filter is a superset of itself and any filters that specify UUIDs. 482 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mIBeaconWithoutUuidFilter)).isTrue(); 483 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mIBeaconWithUuidFilter)).isTrue(); 484 485 // Non-iBeacon filters should never be supersets. 486 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneEidFilter)).isFalse(); 487 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneUrlFilter)).isFalse(); 488 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneUidFilter)).isFalse(); 489 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mWearFilter)).isFalse(); 490 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mSmartSetupFilter)).isFalse(); 491 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mChromecastFilter)).isFalse(); 492 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mFakeFilter1)).isFalse(); 493 assertThat(mIBeaconWithoutUuidFilter.isSuperset(mFakeFilter2)).isFalse(); 494 } 495 496 @Test mixedFilterIsSuperset()497 public void mixedFilterIsSuperset() { 498 // Compare service data vs manufacturer data filters to verify we detect supersets 499 // correctly in filters that aren't for iBeacon and Eddystone. 500 assertThat(mWearFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse(); 501 assertThat(mSmartSetupFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse(); 502 assertThat(mChromecastFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse(); 503 504 assertThat(mWearFilter.isSuperset(mEddystoneFilter)).isFalse(); 505 assertThat(mSmartSetupFilter.isSuperset(mEddystoneFilter)).isFalse(); 506 assertThat(mChromecastFilter.isSuperset(mEddystoneFilter)).isFalse(); 507 508 assertThat(mWearFilter.isSuperset(mEddystoneUidFilter)).isFalse(); 509 assertThat(mSmartSetupFilter.isSuperset(mEddystoneUidFilter)).isFalse(); 510 assertThat(mChromecastFilter.isSuperset(mEddystoneUidFilter)).isFalse(); 511 512 assertThat(mWearFilter.isSuperset(mEddystoneEidFilter)).isFalse(); 513 assertThat(mSmartSetupFilter.isSuperset(mEddystoneEidFilter)).isFalse(); 514 assertThat(mChromecastFilter.isSuperset(mEddystoneEidFilter)).isFalse(); 515 516 assertThat(mWearFilter.isSuperset(mEddystoneUrlFilter)).isFalse(); 517 assertThat(mSmartSetupFilter.isSuperset(mEddystoneUrlFilter)).isFalse(); 518 assertThat(mChromecastFilter.isSuperset(mEddystoneUrlFilter)).isFalse(); 519 520 assertThat(mWearFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse(); 521 assertThat(mSmartSetupFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse(); 522 assertThat(mChromecastFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse(); 523 524 assertThat(mWearFilter.isSuperset(mChromecastFilter)).isFalse(); 525 assertThat(mSmartSetupFilter.isSuperset(mChromecastFilter)).isFalse(); 526 assertThat(mSmartSetupFilter.isSuperset(mWearFilter)).isFalse(); 527 assertThat(mChromecastFilter.isSuperset(mWearFilter)).isFalse(); 528 529 assertThat(mFakeFilter1.isSuperset(mFakeFilter2)).isTrue(); 530 assertThat(mFakeFilter2.isSuperset(mFakeFilter1)).isFalse(); 531 assertThat(mSmartSetupFilter.isSuperset(mFakeSmartSetupSubsetFilter)).isTrue(); 532 assertThat(mSmartSetupFilter.isSuperset(mFakeSmartSetupNotSubsetFilter)).isFalse(); 533 534 assertThat(mEddystoneFilter.isSuperset(mEddystoneWithDeviceNameFilter)).isTrue(); 535 assertThat(mEddystoneFilter.isSuperset(mEddystoneWithDeviceAddressFilter)).isTrue(); 536 assertThat(mEddystoneWithDeviceAddressFilter.isSuperset(mEddystoneFilter)).isFalse(); 537 538 assertThat(mChromecastFilter.isSuperset(mServiceUuidWithMaskFilter1)).isTrue(); 539 assertThat(mServiceUuidWithMaskFilter2.isSuperset(mServiceUuidWithMaskFilter1)).isFalse(); 540 assertThat(mServiceUuidWithMaskFilter1.isSuperset(mServiceUuidWithMaskFilter2)).isTrue(); 541 assertThat(mEddystoneFilter.isSuperset(mServiceUuidWithMaskFilter1)).isFalse(); 542 } 543 544 @Test toOsFilter_getTheSameFilterParameter()545 public void toOsFilter_getTheSameFilterParameter() { 546 BleFilter nearbyFilter = createTestFilter(); 547 ScanFilter osFilter = nearbyFilter.toOsFilter(); 548 assertFilterValuesEqual(nearbyFilter, osFilter); 549 } 550 551 @Test describeContents()552 public void describeContents() { 553 BleFilter nearbyFilter = createTestFilter(); 554 assertThat(nearbyFilter.describeContents()).isEqualTo(0); 555 } 556 557 @Test testHashCode()558 public void testHashCode() { 559 BleFilter nearbyFilter = createTestFilter(); 560 BleFilter compareFilter = new BleFilter("BERT", "00:11:22:33:AA:BB", 561 new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")), 562 new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF")), 563 ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), 564 new byte[] {0x51, 0x64}, new byte[] {0x00, (byte) 0xFF}, 565 BluetoothAssignedNumbers.GOOGLE, new byte[] {0x00, 0x10}, 566 new byte[] {0x00, (byte) 0xFF}); 567 assertThat(nearbyFilter.hashCode()).isEqualTo(compareFilter.hashCode()); 568 } 569 570 @Test testToString()571 public void testToString() { 572 BleFilter nearbyFilter = createTestFilter(); 573 assertThat(nearbyFilter.toString()).isEqualTo("BleFilter [deviceName=BERT," 574 + " deviceAddress=00:11:22:33:AA:BB, uuid=0000fea0-0000-1000-8000-00805f9b34fb," 575 + " uuidMask=0fffffff-ffff-0fff-ffff-ffffffffffff," 576 + " serviceDataUuid=0000110b-0000-1000-8000-00805f9b34fb," 577 + " serviceData=[81, 100], serviceDataMask=[0, -1]," 578 + " manufacturerId=224, manufacturerData=[0, 16], manufacturerDataMask=[0, -1]]"); 579 } 580 581 @Test testParcel()582 public void testParcel() { 583 BleFilter nearbyFilter = createTestFilter(); 584 Parcel parcel = Parcel.obtain(); 585 nearbyFilter.writeToParcel(parcel, 0); 586 parcel.setDataPosition(0); 587 BleFilter compareFilter = BleFilter.CREATOR.createFromParcel( 588 parcel); 589 parcel.recycle(); 590 assertThat(compareFilter.getDeviceName()).isEqualTo("BERT"); 591 } 592 593 @Test testCreatorNewArray()594 public void testCreatorNewArray() { 595 BleFilter[] nearbyFilters = BleFilter.CREATOR.newArray(2); 596 assertThat(nearbyFilters.length).isEqualTo(2); 597 } 598 matches( BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecord)599 private static boolean matches( 600 BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecord) { 601 return filter.matches(new BleSighting(device, 602 bleRecord, rssi, 0 /* timestampNanos */)); 603 } 604 assertFilterValuesEqual(BleFilter nearbyFilter, ScanFilter osFilter)605 private static void assertFilterValuesEqual(BleFilter nearbyFilter, ScanFilter osFilter) { 606 assertThat(osFilter.getDeviceAddress()).isEqualTo(nearbyFilter.getDeviceAddress()); 607 assertThat(osFilter.getDeviceName()).isEqualTo(nearbyFilter.getDeviceName()); 608 609 assertThat(osFilter.getManufacturerData()).isEqualTo(nearbyFilter.getManufacturerData()); 610 assertThat(osFilter.getManufacturerDataMask()) 611 .isEqualTo(nearbyFilter.getManufacturerDataMask()); 612 assertThat(osFilter.getManufacturerId()).isEqualTo(nearbyFilter.getManufacturerId()); 613 614 assertThat(osFilter.getServiceData()).isEqualTo(nearbyFilter.getServiceData()); 615 assertThat(osFilter.getServiceDataMask()).isEqualTo(nearbyFilter.getServiceDataMask()); 616 assertThat(osFilter.getServiceDataUuid()).isEqualTo(nearbyFilter.getServiceDataUuid()); 617 618 assertThat(osFilter.getServiceUuid()).isEqualTo(nearbyFilter.getServiceUuid()); 619 assertThat(osFilter.getServiceUuidMask()).isEqualTo(nearbyFilter.getServiceUuidMask()); 620 } 621 assertMatches( BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecordBytes)622 private static void assertMatches( 623 BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecordBytes) { 624 625 // Device match. 626 if (filter.getDeviceAddress() != null 627 && (device == null || !filter.getDeviceAddress().equals(device.getAddress()))) { 628 fail("Filter specified a device address (" 629 + filter.getDeviceAddress() 630 + ") which doesn't match the actual value: [" 631 + (device == null ? "null device" : device.getAddress()) 632 + "]"); 633 } 634 635 // BLE record is null but there exist filters on it. 636 BleRecord bleRecord = BleRecord.parseFromBytes(bleRecordBytes); 637 if (bleRecord == null 638 && (filter.getDeviceName() != null 639 || filter.getServiceUuid() != null 640 || filter.getManufacturerData() != null 641 || filter.getServiceData() != null)) { 642 fail( 643 "The bleRecordBytes given parsed to a null bleRecord, but the filter" 644 + "has a non-null field which depends on the scan record"); 645 } 646 647 // Local name match. 648 if (filter.getDeviceName() != null 649 && !filter.getDeviceName().equals(bleRecord.getDeviceName())) { 650 fail( 651 "The filter's device name (" 652 + filter.getDeviceName() 653 + ") doesn't match the scan record device name (" 654 + bleRecord.getDeviceName() 655 + ")"); 656 } 657 658 // UUID match. 659 if (filter.getServiceUuid() != null 660 && !matchesServiceUuids(filter.getServiceUuid(), 661 filter.getServiceUuidMask(), bleRecord.getServiceUuids())) { 662 fail("The filter specifies a service UUID " 663 + "but it doesn't match what's in the scan record"); 664 } 665 666 // Service data match 667 if (filter.getServiceDataUuid() != null 668 && !BleFilter.matchesPartialData( 669 filter.getServiceData(), 670 filter.getServiceDataMask(), 671 bleRecord.getServiceData(filter.getServiceDataUuid()))) { 672 fail( 673 "The filter's service data doesn't match what's in the scan record.\n" 674 + "Service data: " 675 + byteString(filter.getServiceData()) 676 + "\n" 677 + "Service data UUID: " 678 + filter.getServiceDataUuid().toString() 679 + "\n" 680 + "Service data mask: " 681 + byteString(filter.getServiceDataMask()) 682 + "\n" 683 + "Scan record service data: " 684 + byteString(bleRecord.getServiceData(filter.getServiceDataUuid())) 685 + "\n" 686 + "Scan record data map:\n" 687 + byteString(bleRecord.getServiceData())); 688 } 689 690 // Manufacturer data match. 691 if (filter.getManufacturerId() >= 0 692 && !BleFilter.matchesPartialData( 693 filter.getManufacturerData(), 694 filter.getManufacturerDataMask(), 695 bleRecord.getManufacturerSpecificData(filter.getManufacturerId()))) { 696 fail( 697 "The filter's manufacturer data doesn't match what's in the scan record.\n" 698 + "Manufacturer ID: " 699 + filter.getManufacturerId() 700 + "\n" 701 + "Manufacturer data: " 702 + byteString(filter.getManufacturerData()) 703 + "\n" 704 + "Manufacturer data mask: " 705 + byteString(filter.getManufacturerDataMask()) 706 + "\n" 707 + "Scan record manufacturer-specific data: " 708 + byteString(bleRecord.getManufacturerSpecificData( 709 filter.getManufacturerId())) 710 + "\n" 711 + "Manufacturer data array:\n" 712 + byteString(bleRecord.getManufacturerSpecificData())); 713 } 714 715 // All filters match. 716 assertThat( 717 matches(filter, device, rssi, bleRecordBytes)).isTrue(); 718 } 719 720 byteString(byte[] bytes)721 private static String byteString(byte[] bytes) { 722 if (bytes == null) { 723 return "[null]"; 724 } else { 725 final char[] hexArray = "0123456789ABCDEF".toCharArray(); 726 char[] hexChars = new char[bytes.length * 2]; 727 for (int i = 0; i < bytes.length; i++) { 728 int v = bytes[i] & 0xFF; 729 hexChars[i * 2] = hexArray[v >>> 4]; 730 hexChars[i * 2 + 1] = hexArray[v & 0x0F]; 731 } 732 return new String(hexChars); 733 } 734 } 735 byteString(Map<ParcelUuid, byte[]> bytesMap)736 private static String byteString(Map<ParcelUuid, byte[]> bytesMap) { 737 StringBuilder builder = new StringBuilder(); 738 for (Map.Entry<ParcelUuid, byte[]> entry : bytesMap.entrySet()) { 739 builder.append(builder.toString().isEmpty() ? " " : "\n "); 740 builder.append(entry.getKey().toString()); 741 builder.append(" --> "); 742 builder.append(byteString(entry.getValue())); 743 } 744 return builder.toString(); 745 } 746 byteString(SparseArray<byte[]> bytesArray)747 private static String byteString(SparseArray<byte[]> bytesArray) { 748 StringBuilder builder = new StringBuilder(); 749 for (int i = 0; i < bytesArray.size(); i++) { 750 builder.append(builder.toString().isEmpty() ? " " : "\n "); 751 builder.append(byteString(bytesArray.valueAt(i))); 752 } 753 return builder.toString(); 754 } 755 createTestFilter()756 private static BleFilter createTestFilter() { 757 BleFilter.Builder builder = new BleFilter.Builder(); 758 builder 759 .setServiceUuid( 760 new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")), 761 new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF"))) 762 .setDeviceAddress("00:11:22:33:AA:BB") 763 .setDeviceName("BERT") 764 .setManufacturerData( 765 BluetoothAssignedNumbers.GOOGLE, 766 new byte[] {0x00, 0x10}, 767 new byte[] {0x00, (byte) 0xFF}) 768 .setServiceData( 769 ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), 770 new byte[] {0x51, 0x64}, 771 new byte[] {0x00, (byte) 0xFF}); 772 return builder.build(); 773 } 774 775 // ref to beacon.decode.BeaconFilterBuilder.eddystoneFilter() createEddystoneFilter()776 private static BleFilter createEddystoneFilter() { 777 return new BleFilter.Builder().setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID).build(); 778 } 779 // ref to beacon.decode.BeaconFilterBuilder.eddystoneUidFilter() createEddystoneUidFilter()780 private static BleFilter createEddystoneUidFilter() { 781 return new BleFilter.Builder() 782 .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID) 783 .setServiceData( 784 EDDYSTONE_SERVICE_DATA_PARCELUUID, new byte[] {(short) 0x00}, 785 new byte[] {(byte) 0xf0}) 786 .build(); 787 } 788 789 // ref to beacon.decode.BeaconFilterBuilder.eddystoneUrlFilter() createEddystoneUrlFilter()790 private static BleFilter createEddystoneUrlFilter() { 791 return new BleFilter.Builder() 792 .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID) 793 .setServiceData( 794 EDDYSTONE_SERVICE_DATA_PARCELUUID, 795 new byte[] {(short) 0x10}, new byte[] {(byte) 0xf0}) 796 .build(); 797 } 798 799 // ref to beacon.decode.BeaconFilterBuilder.eddystoneEidFilter() createEddystoneEidFilter()800 private static BleFilter createEddystoneEidFilter() { 801 return new BleFilter.Builder() 802 .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID) 803 .setServiceData( 804 EDDYSTONE_SERVICE_DATA_PARCELUUID, 805 new byte[] {(short) 0x30}, new byte[] {(byte) 0xf0}) 806 .build(); 807 } 808 809 // ref to beacon.decode.BeaconFilterBuilder.iBeaconWithoutUuidFilter() createIBeaconWithoutUuidFilter()810 private static BleFilter createIBeaconWithoutUuidFilter() { 811 byte[] data = {(byte) 0x02, (byte) 0x15}; 812 byte[] mask = {(byte) 0xff, (byte) 0xff}; 813 814 return new BleFilter.Builder().setManufacturerData((short) 0x004C, data, mask).build(); 815 } 816 817 // ref to beacon.decode.BeaconFilterBuilder.iBeaconWithUuidFilter() createIBeaconWithUuidFilter()818 private static BleFilter createIBeaconWithUuidFilter() { 819 byte[] data = getFilterData(ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB")); 820 byte[] mask = getFilterMask(ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB")); 821 822 return new BleFilter.Builder().setManufacturerData((short) 0x004C, data, mask).build(); 823 } 824 825 // Ref to beacon.decode.AppleBeaconDecoder.getFilterData getFilterData(ParcelUuid uuid)826 private static byte[] getFilterData(ParcelUuid uuid) { 827 byte[] data = new byte[18]; 828 data[0] = (byte) 0x02; 829 data[1] = (byte) 0x15; 830 // Check if UUID is needed in data 831 if (uuid != null) { 832 // Convert UUID to array in big endian order 833 byte[] uuidBytes = uuidToByteArray(uuid); 834 for (int i = 0; i < 16; i++) { 835 // Adding uuid bytes in big-endian order to match iBeacon format 836 data[i + 2] = uuidBytes[i]; 837 } 838 } 839 return data; 840 } 841 842 // Ref to beacon.decode.AppleBeaconDecoder.getFilterMask getFilterMask(ParcelUuid uuid)843 private static byte[] getFilterMask(ParcelUuid uuid) { 844 byte[] mask = new byte[18]; 845 mask[0] = (byte) 0xff; 846 mask[1] = (byte) 0xff; 847 // Check if UUID is needed in data 848 if (uuid != null) { 849 for (int i = 0; i < 16; i++) { 850 mask[i + 2] = (byte) 0xff; 851 } 852 } 853 return mask; 854 } 855 856 // Ref to beacon.decode.AppleBeaconDecoder.uuidToByteArray uuidToByteArray(ParcelUuid uuid)857 private static byte[] uuidToByteArray(ParcelUuid uuid) { 858 ByteBuffer bb = ByteBuffer.wrap(new byte[16]); 859 bb.putLong(uuid.getUuid().getMostSignificantBits()); 860 bb.putLong(uuid.getUuid().getLeastSignificantBits()); 861 return bb.array(); 862 } 863 matchesServiceUuids( ParcelUuid uuid, ParcelUuid parcelUuidMask, List<ParcelUuid> uuids)864 private static boolean matchesServiceUuids( 865 ParcelUuid uuid, ParcelUuid parcelUuidMask, List<ParcelUuid> uuids) { 866 if (uuid == null) { 867 return true; 868 } 869 870 for (ParcelUuid parcelUuid : uuids) { 871 UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); 872 if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { 873 return true; 874 } 875 } 876 return false; 877 } 878 879 // Check if the uuid pattern matches the particular service uuid. matchesServiceUuid(UUID uuid, UUID mask, UUID data)880 private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { 881 if (mask == null) { 882 return uuid.equals(data); 883 } 884 if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) 885 != (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { 886 return false; 887 } 888 return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) 889 == (data.getMostSignificantBits() & mask.getMostSignificantBits())); 890 } 891 } 892