xref: /aosp_15_r20/cts/tests/tests/bluetooth/src/android/bluetooth/cts/ScanFilterTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2015 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.cts;
18 
19 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
20 import static android.Manifest.permission.BLUETOOTH_SCAN;
21 import static android.bluetooth.BluetoothStatusCodes.FEATURE_SUPPORTED;
22 
23 import static org.junit.Assert.assertArrayEquals;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertThrows;
28 import static org.junit.Assert.assertTrue;
29 
30 import android.bluetooth.BluetoothAdapter;
31 import android.bluetooth.BluetoothAssignedNumbers.OrganizationId;
32 import android.bluetooth.BluetoothDevice;
33 import android.bluetooth.le.ScanFilter;
34 import android.bluetooth.le.ScanResult;
35 import android.bluetooth.le.TransportBlockFilter;
36 import android.bluetooth.test_utils.Permissions;
37 import android.content.Context;
38 import android.content.pm.PackageManager;
39 import android.os.Parcel;
40 import android.os.ParcelUuid;
41 
42 import androidx.test.ext.junit.runners.AndroidJUnit4;
43 import androidx.test.filters.SmallTest;
44 import androidx.test.platform.app.InstrumentationRegistry;
45 
46 import com.android.compatibility.common.util.CddTest;
47 
48 import org.junit.After;
49 import org.junit.Assume;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.util.List;
55 
56 /**
57  * Unit test cases for Bluetooth LE scan filters.
58  *
59  * <p>To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w
60  * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
61  */
62 @RunWith(AndroidJUnit4.class)
63 @SmallTest
64 public class ScanFilterTest {
65 
66     private static final String LOCAL_NAME = "Ped";
67     private static final String DEVICE_MAC = "01:02:03:04:05:AB";
68     private static final String UUID1 = "0000110a-0000-1000-8000-00805f9b34fb";
69     private static final String UUID2 = "0000110b-0000-1000-8000-00805f9b34fb";
70     private static final String UUID3 = "0000110c-0000-1000-8000-00805f9b34fb";
71     private static final int AD_TYPE_RESOLVABLE_SET_IDENTIFIER = 0x2e;
72 
73     private ScanResult mScanResult;
74     private ScanFilter.Builder mFilterBuilder;
75     private BluetoothAdapter mBluetoothAdapter;
76 
77     @Before
setUp()78     public void setUp() {
79         Context context = InstrumentationRegistry.getInstrumentation().getContext();
80         Assume.assumeTrue(TestUtils.isBleSupported(context));
81 
82         byte[] scanRecord =
83                 new byte[] {
84                     0x02,
85                     0x01,
86                     0x1a, // advertising flags
87                     0x05,
88                     0x02,
89                     0x0b,
90                     0x11,
91                     0x0a,
92                     0x11, // 16 bit service uuids
93                     0x04,
94                     0x09,
95                     0x50,
96                     0x65,
97                     0x64, // setName
98                     0x02,
99                     0x0A,
100                     (byte) 0xec, // tx power level
101                     0x05,
102                     0x16,
103                     0x0b,
104                     0x11,
105                     0x50,
106                     0x64, // service data
107                     0x05,
108                     (byte) 0xff,
109                     (byte) 0xe0,
110                     0x00,
111                     0x02,
112                     0x15, // manufacturer specific data
113                     0x05,
114                     0x14,
115                     0x0c,
116                     0x11,
117                     0x0a,
118                     0x11, // 16 bit service solicitation uuids
119                     0x03,
120                     0x50,
121                     0x01,
122                     0x02, // an unknown data type won't cause trouble
123                     0x07,
124                     0x2E,
125                     0x01,
126                     0x02,
127                     0x03,
128                     0x04,
129                     0x05,
130                     0x06 // resolvable set identifier
131                 };
132 
133         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
134         if (mBluetoothAdapter == null) {
135             // Bluetooth is not supported
136             assertFalse(
137                     context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH));
138         } else {
139             assertTrue(
140                     context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH));
141             BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(DEVICE_MAC);
142             mScanResult =
143                     new ScanResult(
144                             device, TestUtils.parseScanRecord(scanRecord), -10, 1397545200000000L);
145             mFilterBuilder = new ScanFilter.Builder();
146             TestUtils.adoptPermissionAsShellUid(BLUETOOTH_PRIVILEGED);
147         }
148     }
149 
150     @After
tearDown()151     public void tearDown() {
152         if (mFilterBuilder != null) {
153             TestUtils.dropPermissionAsShellUid();
154         }
155     }
156 
157     @CddTest(requirements = {"7.4.3/C-2-1"})
158     @Test
setNameFilter()159     public void setNameFilter() {
160         ScanFilter filter = mFilterBuilder.setDeviceName(LOCAL_NAME).build();
161         assertEquals(LOCAL_NAME, filter.getDeviceName());
162         assertTrue("setName filter fails", filter.matches(mScanResult));
163 
164         filter = mFilterBuilder.setDeviceName("Pem").build();
165         assertFalse("setName filter fails", filter.matches(mScanResult));
166     }
167 
168     @CddTest(requirements = {"7.4.3/C-2-1"})
169     @Test
deviceAddressFilter()170     public void deviceAddressFilter() {
171         ScanFilter filter = mFilterBuilder.setDeviceAddress(DEVICE_MAC).build();
172         assertEquals(DEVICE_MAC, filter.getDeviceAddress());
173         assertTrue("device filter fails", filter.matches(mScanResult));
174 
175         filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
176         assertFalse("device filter fails", filter.matches(mScanResult));
177     }
178 
179     @CddTest(requirements = {"7.4.3/C-2-1"})
180     @Test
setServiceUuidFilter()181     public void setServiceUuidFilter() {
182         ScanFilter filter = mFilterBuilder.setServiceUuid(ParcelUuid.fromString(UUID1)).build();
183         assertEquals(UUID1, filter.getServiceUuid().toString());
184         assertTrue("uuid filter fails", filter.matches(mScanResult));
185 
186         filter = mFilterBuilder.setServiceUuid(ParcelUuid.fromString(UUID3)).build();
187         assertEquals(UUID3, filter.getServiceUuid().toString());
188         assertFalse("uuid filter fails", filter.matches(mScanResult));
189 
190         ParcelUuid mask = ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
191         filter = mFilterBuilder.setServiceUuid(ParcelUuid.fromString(UUID3), mask).build();
192         assertEquals(mask.toString(), filter.getServiceUuidMask().toString());
193         assertTrue("uuid filter fails", filter.matches(mScanResult));
194     }
195 
196     @CddTest(requirements = {"7.4.3/C-2-1"})
197     @Test
setServiceSolicitationUuidFilter()198     public void setServiceSolicitationUuidFilter() {
199         ScanFilter filter =
200                 mFilterBuilder.setServiceSolicitationUuid(ParcelUuid.fromString(UUID1)).build();
201         assertEquals(UUID1, filter.getServiceSolicitationUuid().toString());
202         assertTrue("uuid filter fails", filter.matches(mScanResult));
203 
204         filter = mFilterBuilder.setServiceSolicitationUuid(ParcelUuid.fromString(UUID2)).build();
205         assertEquals(UUID2, filter.getServiceSolicitationUuid().toString());
206         assertFalse("uuid filter fails", filter.matches(mScanResult));
207 
208         ParcelUuid mask = ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
209         filter =
210                 mFilterBuilder
211                         .setServiceSolicitationUuid(ParcelUuid.fromString(UUID3), mask)
212                         .build();
213         assertEquals(mask.toString(), filter.getServiceSolicitationUuidMask().toString());
214         assertTrue("uuid filter fails", filter.matches(mScanResult));
215     }
216 
217     @CddTest(requirements = {"7.4.3/C-2-1"})
218     @Test
setServiceDataFilter()219     public void setServiceDataFilter() {
220         byte[] setServiceData = new byte[] {0x50, 0x64};
221         ParcelUuid serviceDataUuid = ParcelUuid.fromString(UUID2);
222         ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build();
223         assertEquals(serviceDataUuid, filter.getServiceDataUuid());
224         assertTrue("service data filter fails", filter.matches(mScanResult));
225 
226         byte[] emptyData = new byte[0];
227         filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build();
228         assertTrue("service data filter fails", filter.matches(mScanResult));
229 
230         byte[] prefixData = new byte[] {0x50};
231         filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build();
232         assertTrue("service data filter fails", filter.matches(mScanResult));
233 
234         byte[] nonMatchData = new byte[] {0x51, 0x64};
235         byte[] mask = new byte[] {(byte) 0x00, (byte) 0xFF};
236         filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build();
237         assertEquals(nonMatchData, filter.getServiceData());
238         assertEquals(mask, filter.getServiceDataMask());
239         assertTrue("partial service data filter fails", filter.matches(mScanResult));
240 
241         filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build();
242         assertFalse("service data filter fails", filter.matches(mScanResult));
243     }
244 
245     @CddTest(requirements = {"7.4.3/C-2-1"})
246     @Test
setManufacturerSpecificData()247     public void setManufacturerSpecificData() {
248         byte[] manufacturerData = new byte[] {0x02, 0x15};
249         int manufacturerId = 0xE0;
250         ScanFilter filter =
251                 mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
252         assertEquals(manufacturerId, filter.getManufacturerId());
253         assertEquals(manufacturerData, filter.getManufacturerData());
254         assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
255 
256         byte[] emptyData = new byte[0];
257         filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build();
258         assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
259 
260         byte[] prefixData = new byte[] {0x02};
261         filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build();
262         assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
263 
264         // Test data mask
265         byte[] nonMatchData = new byte[] {0x02, 0x14};
266         filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
267         assertFalse("manufacturer data filter fails", filter.matches(mScanResult));
268         byte[] mask = new byte[] {(byte) 0xFF, (byte) 0x00};
269         filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
270         assertEquals(manufacturerId, filter.getManufacturerId());
271         assertEquals(nonMatchData, filter.getManufacturerData());
272         assertEquals(mask, filter.getManufacturerDataMask());
273         assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
274     }
275 
276     @CddTest(requirements = {"7.4.3/C-2-1"})
277     @Test
setAdvertisingDataTypeWithData()278     public void setAdvertisingDataTypeWithData() {
279         byte[] adData = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
280         byte[] adDataMask = {
281             (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
282         };
283         ScanFilter filter =
284                 mFilterBuilder
285                         .setAdvertisingDataTypeWithData(
286                                 AD_TYPE_RESOLVABLE_SET_IDENTIFIER, adData, adDataMask)
287                         .build();
288         assertEquals(AD_TYPE_RESOLVABLE_SET_IDENTIFIER, filter.getAdvertisingDataType());
289         TestUtils.assertArrayEquals(adData, filter.getAdvertisingData());
290         TestUtils.assertArrayEquals(adDataMask, filter.getAdvertisingDataMask());
291         assertTrue("advertising data filter fails", filter.matches(mScanResult));
292         filter = mFilterBuilder.setAdvertisingDataTypeWithData(0x01, adData, adDataMask).build();
293         assertFalse("advertising data filter fails", filter.matches(mScanResult));
294         byte[] nonMatchAdData = {0x01, 0x02, 0x04, 0x04, 0x05, 0x06};
295         filter =
296                 mFilterBuilder
297                         .setAdvertisingDataTypeWithData(
298                                 AD_TYPE_RESOLVABLE_SET_IDENTIFIER, nonMatchAdData, adDataMask)
299                         .build();
300         assertFalse("advertising data filter fails", filter.matches(mScanResult));
301     }
302 
303     @CddTest(requirements = {"7.4.3/C-2-1"})
304     @Test
readWriteParcel()305     public void readWriteParcel() {
306         ScanFilter filter = mFilterBuilder.build();
307         testReadWriteParcelForFilter(filter);
308 
309         filter = mFilterBuilder.setDeviceName(LOCAL_NAME).build();
310         testReadWriteParcelForFilter(filter);
311 
312         filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
313         testReadWriteParcelForFilter(filter);
314 
315         filter = mFilterBuilder.setServiceUuid(ParcelUuid.fromString(UUID3)).build();
316         testReadWriteParcelForFilter(filter);
317 
318         filter =
319                 mFilterBuilder
320                         .setServiceUuid(
321                                 ParcelUuid.fromString(UUID3),
322                                 ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
323                         .build();
324         testReadWriteParcelForFilter(filter);
325 
326         byte[] serviceData = new byte[] {0x50, 0x64};
327 
328         ParcelUuid serviceDataUuid = ParcelUuid.fromString(UUID2);
329         filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build();
330         testReadWriteParcelForFilter(filter);
331 
332         filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
333         testReadWriteParcelForFilter(filter);
334 
335         byte[] serviceDataMask = new byte[] {(byte) 0xFF, (byte) 0xFF};
336         filter =
337                 mFilterBuilder
338                         .setServiceData(serviceDataUuid, serviceData, serviceDataMask)
339                         .build();
340         testReadWriteParcelForFilter(filter);
341 
342         byte[] manufacturerData = new byte[] {0x02, 0x15};
343         int manufacturerId = 0xE0;
344         filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
345         testReadWriteParcelForFilter(filter);
346 
347         filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
348         testReadWriteParcelForFilter(filter);
349 
350         byte[] manufacturerDataMask = new byte[] {(byte) 0xFF, (byte) 0xFF};
351         filter =
352                 mFilterBuilder
353                         .setManufacturerData(manufacturerId, manufacturerData, manufacturerDataMask)
354                         .build();
355         testReadWriteParcelForFilter(filter);
356     }
357 
358     @CddTest(requirements = {"7.4.3/C-2-1"})
359     @Test
describeContents()360     public void describeContents() {
361         final int expected = 0;
362         assertEquals(expected, new ScanFilter.Builder().build().describeContents());
363     }
364 
365     @CddTest(requirements = {"7.4.3/C-2-1"})
366     @Test
builderSetTransportBlockFilter()367     public void builderSetTransportBlockFilter() {
368         final int orgId = OrganizationId.BLUETOOTH_SIG;
369         final int tdsFlag = 0x2;
370         final int tdsFlagMask = 0b11;
371         final byte[] transportData = new byte[] {0x42, 0x43};
372         final byte[] transportDataMask = new byte[] {0x44, 0x45};
373 
374         TransportBlockFilter transportBlockFilter =
375                 new TransportBlockFilter.Builder(orgId)
376                         .setTdsFlags(tdsFlag, tdsFlagMask)
377                         .setTransportData(transportData, transportDataMask)
378                         .build();
379 
380         Permissions.enforceEachPermissions(
381                 () -> mBluetoothAdapter.getOffloadedTransportDiscoveryDataScanSupported(),
382                 List.of(BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED));
383 
384         try (var p = Permissions.withPermissions(BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED)) {
385             if (mBluetoothAdapter.getOffloadedTransportDiscoveryDataScanSupported()
386                     != FEATURE_SUPPORTED) {
387                 assertThrows(
388                         IllegalArgumentException.class,
389                         () -> mFilterBuilder.setTransportBlockFilter(transportBlockFilter));
390                 // Ignore test when device does not support the feature
391                 Assume.assumeTrue(false);
392                 return;
393             }
394         }
395 
396         Permissions.enforceEachPermissions(
397                 () -> mFilterBuilder.setTransportBlockFilter(transportBlockFilter),
398                 List.of(BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED));
399 
400         final ScanFilter filter;
401         try (var p = Permissions.withPermissions(BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED)) {
402             filter = mFilterBuilder.setTransportBlockFilter(transportBlockFilter).build();
403         }
404 
405         final TransportBlockFilter returnedTransportBlockFilter = filter.getTransportBlockFilter();
406         assertNotNull(returnedTransportBlockFilter);
407         assertEquals(orgId, returnedTransportBlockFilter.getOrgId());
408         assertEquals(tdsFlag, returnedTransportBlockFilter.getTdsFlags());
409         assertEquals(tdsFlagMask, returnedTransportBlockFilter.getTdsFlagsMask());
410         assertArrayEquals(transportData, returnedTransportBlockFilter.getTransportData());
411         assertArrayEquals(transportDataMask, returnedTransportBlockFilter.getTransportDataMask());
412     }
413 
testReadWriteParcelForFilter(ScanFilter filter)414     private void testReadWriteParcelForFilter(ScanFilter filter) {
415         Parcel parcel = Parcel.obtain();
416         filter.writeToParcel(parcel, 0);
417         parcel.setDataPosition(0);
418         ScanFilter filterFromParcel = ScanFilter.CREATOR.createFromParcel(parcel);
419         assertEquals(filter, filterFromParcel);
420     }
421 }
422