背景
个人开发过一种BLE设备有这样一种需求:当设备处于状态A时,广播设备名称A;处于状态B时,广播设备名称B。
问题
我们发现,当Android在进行Ble扫描的时候,扫描回调函数onScanResult中获取的设备名称并未随设备实时改变。
但是当使用nrfConnect进行扫描时,却发现设备广播名称确实是实时改变的。
原因
我们使用如下代码获取BLE设备名称,然而此时device.getName()中的名称是系统缓存的数据,而非实时的广播数据,要想获取实时的设备名称,必须自行解码实时广播数据。
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice device = result.getDevice();
if(null != device && null != device.getName()) {
Log.d("test", device.getName()); // not the real name
}
} @Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
} };
解决
通过分析BLE广播包协议,自行解码出设备名称。
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice device = result.getDevice();
if(null != device && null != device.getScanRecord().getBytes()) {
byte[] scanRecord = device.getScanRecord().getBytes(); // advertised data
String realName = parseDeviceName(scanRecord);
}
} @Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
} };
// return name String(successful) or null(failed)
public static String parseDeviceName(byte[] scanRecord) {
String ret = null;
if(null == scanRecord) {
return ret;
} ByteBuffer buffer = ByteBuffer.wrap(scanRecord).order(ByteOrder.LITTLE_ENDIAN);
while (buffer.remaining() > 2) {
byte length = buffer.get();
if (length == 0)
break; byte type = buffer.get();
length -= 1;
switch (type) {
case 0x01: // Flags
buffer.get(); // flags
length--;
break;
case 0x02: // Partial list of 16-bit UUIDs
case 0x03: // Complete list of 16-bit UUIDs
case 0x14: // List of 16-bit Service Solicitation UUIDs
while (length >= 2) {
buffer.getShort();
length -= 2;
}
break;
case 0x04: // Partial list of 32 bit service UUIDs
case 0x05: // Complete list of 32 bit service UUIDs
while (length >= 4) {
buffer.getInt();
length -= 4;
}
break;
case 0x06: // Partial list of 128-bit UUIDs
case 0x07: // Complete list of 128-bit UUIDs
case 0x15: // List of 128-bit Service Solicitation UUIDs
while (length >= 16) {
long lsb = buffer.getLong();
long msb = buffer.getLong();
length -= 16;
}
break;
case 0x08: // Short local device name
case 0x09: // Complete local device name
byte sb[] = new byte[length];
buffer.get(sb, 0, length);
length = 0;
ret = new String(sb).trim();
return ret;
case (byte) 0xFF: // Manufacturer Specific Data
buffer.getShort();
length -= 2;
break;
default: // skip
break;
}
if (length > 0) {
buffer.position(buffer.position() + length);
}
}
return ret;
}
参考资料:
https://www.race604.com/ble-advertising/