• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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