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