在蓝牙LE连接中使用ScanFilter时没有回调

时间:2019-07-04 14:29:09

标签: android bluetooth

我想设置一个ScanFilter来根据其广告数据扫描BLE设备:名称,服务UUID和其制造数据的第一个字符。问题是:当我使用ScanFilter时,没有回调,这意味着不会调用.onScanResult()。

设备名称正确,因为我已经通过在回调中找到设备进行了测试。当我不使用ScanFilter时,会出现回调,并且可以通过其名称找到该设备。并且它可以成功连接到设备。

  1. 这是我有问题的代码。您可以帮助指出问题吗?

  2. 我还有一个问题:如何为.setManufacturerData()输入三个参数?

    protected void scan(){
    
        List<ScanFilter> filters = new ArrayList<>();
        ScanFilter filterName = new ScanFilter.Builder().setDeviceName(this.mDeviceName).build();
        filters.add(filterName);
        //ScanFilter filterUUID = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("00001814-0000-1000-8000-00805F9B34FB")).build();
        //filters.add(filterUUID);
        //ScanFilter filterManu = new ScanFilter.Builder().setManufacturerData().build();
        //filters.add(filterManu);
    
        ScanSettings settings = (new Builder()).setScanMode(2).build();
        this.mBluetoothLeScanner.startScan(filters, settings, this);
        this.mScanning = true;
    }
    
    @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            BluetoothDevice _device = result.getDevice();
                    Log.i("onScanResult", " callback function is being called");
    
            // find device by name
            if (_device != null && _device.getName() != null && _device.getName().matches(this.mDeviceName) ) {
                this.stopScan();
                this.mDevice = _device;
                Log.i(TAG, "mac address : " + this.mDevice.getAddress() + ", name : " + this.mDevice.getName());
                this.mDeviceFoundLatch.countDown();
            }
    
            long m = mDeviceFoundLatch.getCount();
            Log.i("onResultscallbackLatch", String.valueOf(m));
    
        }
    

我遇到了这些错误:

E/ScanRecord: unable to parse scan record: [2, 1, 6, 3, 2, 20, 24, 2, -1, 82, 20, 9, 83, 116, 114, 105, 100, 97, 108, 121, 122, 101, 114, 32, 73, 78, 83, 73, 71, 72, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

E/BluetoothServiceJni: An exception was thrown by callback 'btgattc_scan_result_cb'.

E/BluetoothServiceJni: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.get(java.lang.Object)' on a null object reference
        at android.bluetooth.le.ScanRecord.getServiceData(ScanRecord.java:121)
        at android.bluetooth.le.ScanFilter.matches(ScanFilter.java:305)
        at com.android.bluetooth.gatt.GattService.matchesFilters(GattService.java:659)
        at com.android.bluetooth.gatt.GattService.onScanResult(GattService.java:611)

这是正确运行的代码。我可以用它来找到设备。但是我想通过制造数据过滤设备。扫描时是否需要设置过滤器?无论如何,这里是否可以通过其制造数据来过滤设备?

protected void scan(){    
        ScanSettings settings = (new Builder()).setScanMode(2).build();
        this.mBluetoothLeScanner.startScan(null, settings, this);
        this.mScanning = true;
    }

    @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            BluetoothDevice _device = result.getDevice();
            if (_device != null && _device.getName() != null && _device.getName().matches(this.mDeviceName) ) {
                this.stopScan();
                this.mDevice = _device;
                this.mDeviceFoundLatch.countDown();
            }
        }

2 个答案:

答案 0 :(得分:0)

确实是设备发送的数据无效。格式基本上是以下顺序:

  • 数据长度(包括字段类型)
  • 字段类型
  • 数据

因此,如果我们拆分数据,则会得到:

2: length 2 bytes
1: field type "flags"
6: data (flags)

3: length 3 bytes
2: field type "partial 16 bit UUID"
20, 24: data (UUID bytes 0x14 0x18, that will be converted into the full UUID 00001814-0000-1000-8000-00805F9B34FB)

2: length 2 bytes
-1 (or 0xff): field type "manufacturer data"
82: data (manufacturer data)

20: length 20 bytes
9: field type "local name complete"
83, 116, 114, 105, 100, 97, 108, 121, 122, 101, 114, 32, 73, 78, 83, 73, 71, 72, 84: data (local name, decoded as "Stridalyzer INSIGHT")
0, 0, ...: ignored data

很遗憾,制造商数据无效。它必须至少为3个字节(类型字段为4个字节),并以2个字节开头作为制造商ID。因此,此时,Android放弃了解析,并创建了一个完全没有数据的 ScanResult

因此,您将没有过滤器的运气。相反,您需要自己对其进行过滤。

答案 1 :(得分:0)

最终使它起作用!这是我的解决方案:

由于制造商数据无效(请参见上面@code的说明),我们不能直接使用Android提供的函数-scanRecord.getManufacturerSpecificData()。相反,我使用以下代码手动解析广告:

@Override
public void onScanResult(int callbackType, ScanResult result) {
    super.onScanResult(callbackType, result);


    BluetoothDevice _device = result.getDevice();
    ScanRecord scanRecord = result.getScanRecord();
    byte[] byte_ScanRocord = scanRecord.getBytes();
    int isLeft = byte_ScanRocord[9];//the 9th byte stores the information that can be used to filter the device
    String byte_ScanRocord_str = Arrays.toString(byte_ScanRocord);
    Log.i("onScanRecordAdv",byte_ScanRocord_str);
    Log.i("onScanRecordisLeft",String.valueOf(isLeft));

    if (_device != null && _device.getName() != null && _device.getName().matches(this.mDeviceName) && isLeft == 76 ) {
        this.stopScan();
        this.mDevice = _device;
        Log.i("InsoleScanner", "mac address : " + this.mDevice.getAddress() + ", name : " + this.mDevice.getName());
        this.mDeviceFoundLatch.countDown();
    }
}