Ble蓝牙广播数据包解析
Ble蓝牙广播数据包解析
项目背景
蓝牙扫描时需做到快速连接指定设备的蓝牙,苹果手机应系统原因,无法获取手机的唯
一字段,故寻常的蓝牙扫描无法指定到具体的单个设备。
经讨论后,得出BLE设备工作的第一步就是向外广播数据。广播数据中带有设备相关的信息。添加
设备的ble蓝牙MAC值添加到广播包的自定义数据字段内容。通过比对MAC值来做到设备的唯一值
匹配。
广播包获取
蓝牙扫描阶段,会返回广播包数据相关的内容
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
}
};
其中byte[] scanRecord 为蓝牙广播数据。
广播包解析
下面是列举的一个广播包解析后的16进制数据
每个包都是 62 字节,数据包中分为有效数据(significant)和无效数据(non-significant)两部
分。
02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 09 03 E7 FE 12
有效数据部分:包含若干个广播数据单元,称为 AD Structure。如图中所示,AD Structure 的组成
是:第一个字节是长度值 Len,表示接下来的 Len 个字节是数据部分。数据部分的第一个字节表示
数据的类型 AD Type,剩下的 Len - 1 个字节是真正的数据 AD data。其中 AD type 非常关键,决
定了 AD Data 的数据代表的是什么和怎么解析
无效数据部分:因为广播包的长度必须是 62 个 byte,如果有效数据部分不到 62 自己,剩下的就
用 0 补全。这部分的数据是无效的,解释的时候,忽略即可。
AD type分类
Flags: TYPE = 0x01。这个数据用来标识设备 LE 物理连接的功能。DATA 是 0 到多个字节的 Flag
值,每个 bit 上用 0 或者 1 来表示是否为 True。如果有任何一个 bit 不为 0,并且广播包是可连接
的,就必须包含此数据。各 bit 的定义如下:
bit 0: LE 有限发现模式
bit 1: LE 普通发现模式
bit 2: 不支持 BR/EDR
bit 3: 对 Same Device Capable(Controller) 同时支持 BLE 和 BR/EDR
bit 4: 对 Same Device Capable(Host) 同时支持 BLE 和 BR/EDR
bit 5..7: 预留
Service UUID: 广播数据中一般都会把设备支持的 GATT Service 广播出来,用来告诉外面本设备所
支持的 Service。有三种类型的 UUID:16 bit, 32bit, 128 bit。广播中,每种类型类型有有两个类
别:完整和非完整的。这样就共有 6 种 AD Type。
非完整的 16 bit UUID 列表: TYPE = 0x02;
完整的 16 bit UUID 列表: TYPE = 0x03;
非完整的 32 bit UUID 列表: TYPE = 0x04;
完整的 32 bit UUID 列表: TYPE = 0x05;
非完整的 128 bit UUID 列表: TYPE = 0x06;
完整的 128 bit UUID 列表: TYPE = 0x07;
Local Name: 设备名字,DATA 是名字的字符串。 Local Name 可以是设备的全名,也可以是设备
名字的缩写,其中缩写必须是全名的前面的若干字符。
设备全名: TYPE = 0x08
设备简称: TYPE = 0x09
TX Power Level: TYPE = 0x0A,表示设备发送广播包的信号强度。DATA 部分是一个字节,表示
-127 到 + 127 dBm。
带外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每个 bit 表示
一个功能:
bit 0: OOB Flag,0 表示没有 OOB 数据,1 表示有
bit 1: 支持 LE
bit 2: 对 Same Device Capable(Host) 同时支持 BLE 和 BR/EDR
bit 3: 地址类型,0 表示公开地址,1 表示随机地址
外设(Slave)连接间隔范围:TYPE = 0x12。数据中定义了 Slave 最大和最小连接间隔,数据包
含 4 个字节:
前 2 字节:定义最小连接间隔,取值范围:0x0006 ~ 0x0C80,而 0xFFFF 表示未定义;
后 2 字节:定义最大连接间隔,同上,不过需要保证最大连接间隔大于或者等于最小连接间隔。
服务搜寻:外围设备可以要请中心设备提供相应的 Service。其数据定义和前面的 Service UUID 类
似:
16 bit UUID 列表: TYPE = 0x14
32 bit UUID 列表: TYPE = 0x??
128 bit UUID 列表: TYPE = 0x15
Service Data: Service 对应的数据。
16 bit UUID Service: TYPE = 0x16, 前 2 字节是 UUID,后面是 Service 的数据;
32 bit UUID Service: TYPE = 0x??, 前 4 字节是 UUID,后面是 Service 的数据;
128 bit UUID Service: TYPE = 0x??, 前 16 字节是 UUID,后面是 Service 的数据;
公开目标地址:TYPE = 0x17,表示希望这个广播包被指定的目标设备处理,此设备绑定了公开地
址,DATA 是目标地址列表,每个地址 6 字节。
随机目标地址:TYPE = 0x18,定义和前一个类似,表示希望这个广播包被指定的目标设备处理,
此设备绑定了随机地址,DATA 是目标地址列表,每个地址 6 字节。
Appearance:TYPE = 0x19,DATA 是表示了设备的外观。
厂商自定义数据: TYPE = 0xFF,厂商自定义的数据中,前两个字节表示厂商 ID,剩下的是厂商自
己按照需求添加,里面的数据内容自己定义。
解析示例
根据解析规则,可分成如下部分:
有效数据
02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54
2D 35 09 03 E7 FE 12 FF 0F 18 0A 18
无效数据
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
其中的有效数据又可分为如下几个数据单元:
02 01 06
14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23
06 08 48 45 54 2D 35
09 03 E7 FE 12 FF 0F 18 0A 18
根据上面定义的AD Type分别解析如下:
第一组数据告诉我们该设备属于LE普通发现模式,不支持BR/EDR;
第二组数据告诉我们该数据为厂商自定义数据,一般是必须解析的,可根据协议规则进行解析
获取对应的所需信息;
第三组数据告诉我们该设备的简称为HET-5,其中对应的字符是查找ASSIC表得出的;
解析代码
public class BleAdParse {
public static ParsedAd parseScanRecodeData(ParsedAd parsedAd ,byte[] adv_data) {
ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN);
while (buffer.remaining() > 2) {
byte length = buffer.get();
if (length == 0)
break;
if (length>buffer.remaining())
break;
byte type = buffer.get();
length -= 1;
switch (type) {
case 0x01: // Flags
parsedAd.flags = buffer.get();
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) {
parsedAd.uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", 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) {
parsedAd.uuids.add(UUID.fromString(String.format(
"%08x-0000-1000-8000-00805f9b34fb", 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();
UUID uuid=new UUID(msb, lsb);
parsedAd.uuids.add(uuid);
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;
parsedAd.localName = new String(sb).trim();
break;
case (byte) 0xFF: // Manufacturer Specific Data
byte sb2[] = new byte[length];
buffer.get(sb2, 0, length);
length = 0;
parsedAd.diyInfo = StringUtil.byte2hex(sb2);;
break;
default: // skip
break;
}
if (length > 0) {
if ((buffer.position()+length)<buffer.capacity()){
buffer.position(buffer.position() + length);
}else {
buffer.position(buffer.capacity());
}
}
}
return parsedAd;
}
}
public class ParsedAd {
public byte flags;
public List<UUID>uuids=new ArrayList<>();
public String localName;
public String diyInfo;
public short manufacturer;
}