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;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容