在蓝牙 LE 连接中使用 ScanFilter 时没有回调
no callback when using ScanFilter in Bluetooth LE Connection
我想设置一个 ScanFilter 以根据其广告数据扫描 BLE 设备:名称、服务 UUID 及其制造数据的第一个字符。问题是:当我使用 ScanFilter 时,没有回调,这意味着没有调用 .onScanResult()。
设备名称是正确的,因为我已经通过在回调中找到设备进行了测试。当我不使用 ScanFilter 时,有回调,可以通过名称找到设备。并且可以成功连接到设备。
这是我有问题的代码。你能帮忙指出它的问题吗?
另外,我还有一个问题:.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: 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 个字节),并以制造商 ID 的 2 个字节开头。所以在这一点上,Android 放弃解析并创建一个 ScanResult,根本没有数据。
所以你不会对过滤器有任何好感。相反,您需要自己过滤它。
终于成功了!这是我的解决方案:
由于制造商数据无效(见上面@code的解释),我们不能直接使用Android-provided函数-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();
}
}
我想设置一个 ScanFilter 以根据其广告数据扫描 BLE 设备:名称、服务 UUID 及其制造数据的第一个字符。问题是:当我使用 ScanFilter 时,没有回调,这意味着没有调用 .onScanResult()。
设备名称是正确的,因为我已经通过在回调中找到设备进行了测试。当我不使用 ScanFilter 时,有回调,可以通过名称找到设备。并且可以成功连接到设备。
这是我有问题的代码。你能帮忙指出它的问题吗?
另外,我还有一个问题:.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: 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 个字节),并以制造商 ID 的 2 个字节开头。所以在这一点上,Android 放弃解析并创建一个 ScanResult,根本没有数据。
所以你不会对过滤器有任何好感。相反,您需要自己过滤它。
终于成功了!这是我的解决方案:
由于制造商数据无效(见上面@code的解释),我们不能直接使用Android-provided函数-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();
}
}