一、引言

理解和分析这个数据包结构(这里面也涉及广播间隔时间的设置,设备广播数据间隔设置长了,会影响设备被发现的效率;设置短时,又响应功耗)。

我们所说的BLE设备,其实是有区分有两种角色 Central 和 Peripheral,也就是中心设备和外围设备。中心设备可以主动连接外围设备,外围设备发送广播或者被中心设备连接。外围通过广播被中心设备发现,广播中带有外围设备自身的相关信息。在日常APP开发中,手机端的BLE一般都是充当中心设备的。

广播包有两种:广播包(Advertising Data)和响应包(Scan Response),其中广播包是每个设备必须广播的,而响应包是可选的。

二、广播的类型

广播的类型一般分为四种,有:

2.1 可连接的非定向广播(Connectable Undirected Event Type)

这种是用途最广的广播类型,包括广播数据和扫描响应数据,它表示当前设备可以接受其他任何设备的连接请求。进行通用广播的设备能够被扫描设备扫描到,或者在接收到连接请求时作为从设备进入一个连接。通用广播可以在没有连接的情况下发出,换句话说,没有主从设备之分。

2.2 可连接的定向广播(Connectable Directed Event Type)

定向广播类型是为了尽可能快的建立连接。这种报文包含两个地址:广播者的地址和发起者的地址。发起者收到发给自己的定向广播报文之后,可以立即发送连接请求作为回应。定向广播类型有特殊的时序要求。完整的广播事件必须每3.75ms重复一次。这一要求使得扫描设备只需扫描3.75ms便可以收到定向广播设备的消息。当然,如此快的发送会让报文充斥着广播信道,进而导致该区域内的其他设备无法进行广播。因此,定向广播不可以持续1.28s以上的时间。如果主机没有主动要求停止,或者连接没有建立,控制器都会自动停止广播。一旦到了1.28s,主机便只能使用间隔长得多的可连接非定向广播让其他设备来连接。

当使用定向广播时,设备不能被主动扫描。此外,定向广播报文的净荷中也不能带有其他附加数据。该净荷只能包含两个必须的地址。

2.3 不可连接的非定向广播(Non-connectable Undirected Event Type)

仅仅发送广播数据,而不想被扫描或者连接。这也是唯一可用于只有发射机而没有接收机设备的广播类型。不可连接广播设备不会进入连接态,因此,它只能根据主机的要求在广播态和就绪态之间切换。

2.4 可扫描的非定向广播(Scannable Undirected Event Type)

又称可发现广播,这种广播不能用于发起连接,但允许其他设备扫描该广播设备。这意味着该设备可以被发现,既可以发送广播数据,也可以响应扫描发送扫描回应数据,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含于广播数据之中,而静态数据可以包含于扫描响应数据之中。

注意:所谓的定向和非定向针对的是广播的对象,如果是针对特定的对象进行广播(在广播包PDU中会包含目标对象的MAC)就是定向广播,反之就是非定向。可连接和不可连接是指是否接受连接请求,如果是不可连接的广播类型,它将不回应连接请求。可扫描广播类型是指回应扫描请求。

三、广播数据格式

每个包都是 31 字节,数据包中分为有效数据(significant)和无效数据(non-significant)两部分。

  • 有效数据部分:包含若干个广播数据单元,称为 AD Structure。如图中所示,AD Structure 的组成是:第一个字节是长度值 Len,表示接下来的 Len 个字节是数据部分。数据部分的第一个字节表示数据的类型 AD Type,剩下的 Len - 1 个字节是真正的数据 AD data。其中 AD type 非常关键,决定了 AD Data 的数据代表的是什么和怎么解析
  • 无效数据部分:因为广播包的长度必须是 31 个 byte,如果有效数据部分不到 31 自己,剩下的就用 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,剩下的是厂商自己按照需求添加,里面的数据内容自己定义。

主要的也就是这些了,更多的可参考SIG官网。

四、广播数据解析

4.1 旧接口(API 21 之前)

在API21之前,Android蓝牙扫描可以使用 BluetoothAdapter的startLeScan来发起扫描。基本用法如下:
BluetoothAdapter:

    //不带过滤搜索
startLeScan(BluetoothAdapter.LeScanCallback callback)
//带UUID过滤搜索
startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
// 解析广播数据
parseScanRecodeData(scanRecord);
}
};

当扫描到设备以后,就会回调 onLeScan(...),这里的参数 scanRecord 就是广播数据,同时包含广播数据和扫描相应数据(如果有的话),所以长度一般就是 62 字节。搜索结果如下:

整个广播数据就在这个scanRecord中,所以需要对scanRecord进行字节解析

public static ParsedAd parseScanRecodeData(byte[] adv_data) {
ParsedAd parsedAd = new ParsedAd();
ByteBuffer buffer = ByteBuffer.wrap(adv_data).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
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();
parsedAd.uuids.add(new UUID(msb, lsb));
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
parsedAd.manufacturer = buffer.getShort();
length -= 2;
break;
default: // skip
break;
}
if (length > 0) {
buffer.position(buffer.position() + length);
}
}
return parsedAd;
}

4.2 新接口

在Android 5.0 (API 21)及之后使用新接口扫描BLE设备,也提供了一些广播数据包的解析接口。新和扫描方法使用的是BluetoothLeScanner,封装成了一个新的类,BluetoothLeScanner还有另一个扫描方法,这个方法就是多了一个扫描过滤 和 扫描设置参数:

    BluetoothLeScanner bluetoothLeScanner=bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(mLeScanCallback);
//扫描过滤 和 扫描设置参数:
bluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, mLeScanCallback) ScanCallback mLeScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.e(TAG, "ScanResult:" + scanResult.toString());
![](http://www.xmamiga.com/wp-content/uploads/2018/10/Android-BLE-Scaner.png)byte[] scanData=result.getScanRecord().getBytes();
//把byte数组转成16进制字符串,方便查看
//Log.e("TAG","onScanResult :"+utils.Bytes2HexString(scanData));//与旧接口一致
Log.e(TAG, "newdevice:" + device.getAddress() + " " + device.getName());
Log.e(TAG, "scanRecord:" + scanResult.getScanRecord().toString());
Log.e(TAG, "scanRecord getManufacturerSpecificData:" + scanResult.getScanRecord().getManufacturerSpecificData().toString());
}
};

搜索结果如下:

手机端运行Log:

涉及接口有:
ScanResult接口

Type Public methods ins
int getAdvertisingSid() Returns the advertising set id.
BluetoothDevice getDevice() Returns the remote Bluetooth device identified by the Bluetooth device address.
int getPeriodicAdvertisingInterval() Returns the periodic advertising interval in units of 1.25ms.
int getRssi() Returns the received signal strength in dBm.
ScanRecord getScanRecord() Returns the scan record, which is a combination of advertisement and scan response.

ScanRecord接口

Type Public methods ins
int getAdvertiseFlags() Returns the advertising flags indicating the discoverable mode and capability of the device.
byte[] getBytes() Returns raw bytes of scan record.
String getDeviceName() Returns the local name of the BLE device.
SparseArray<byte[]> getManufacturerSpecificData() Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific data.
byte[] getManufacturerSpecificData(int manufacturerId) Returns the manufacturer specific data associated with the manufacturer id.
byte[] getServiceData(ParcelUuid serviceDataUuid) Returns the service data byte array associated with the serviceUuid.
List≶ParcelUuid&gt; getServiceUuids() Returns a list of service UUIDs within the advertisement that are used to identify the bluetooth GATT services.

五、总结

BLE广播数据包并不复杂,数来数去也有那几个字节,用心地去理解,就可以解析出自已想要的数据。

Android的BLE广播数据包解析---Android系列, 蓝牙技术(含BLE)的更多相关文章

  1. 蓝牙Beacon广播数据包格式以及解析

    目录 1. 获取原始蓝牙广播包 2. 安装WireShark软件 3. 分析Beacon广播包数据 3.1 第一个数据包格式 3.2 第二个数据包格式 3.3 Android程序开发中的蓝牙广播包 4 ...

  2. 蓝牙BLE: 蓝牙4.0 BLE广播数据解析(转)

    BLE 设备工作的第一步就是向外广播数据.广播数据中带有设备相关的信息.本文主要说一下 BLE 的广播中的数据的规范以及广播包的解析. 1. 广播模式 BLE 中有两种角色 Central 和 Per ...

  3. 基于tcpdump的Android智能移动终端数据包捕获完整解决方案

    如何在Android智能手机上捕获数据包? 本文由CSDN-蚍蜉撼青松[主页:http://blog.csdn.net/howeverpf]原创,转载请注明出处! 当前Android系统越来越流行,无 ...

  4. 一分钟读懂低功耗蓝牙(BLE)连接数据包

    一分钟读懂低功耗蓝牙(BLE)连接数据包 1.概述 BLE 连接过程中有三个重要的数据包:SCAN_REQ, SCAN_RSP 和 CONNECT_REQ. SCAN_REQ: 扫描请求,由主设备(M ...

  5. GPS数据包格式及数据包解析

    GPS数据包解析 GPS数据包解析 目的 GPS数据类型及格式 数据格式 数据解释 解析代码 结构体定义 GPRMC解析函数 GPGGA解析函数 测试样例输出 gps数据包格式 gps数据解析 车联网 ...

  6. 一个C++版的网络数据包解析策略

    C++版的网络数据包解析策略(升级版) 一.数据包格式形如下图 二.代码 int ReceiveFromRemoteEndPoint() { int nPackageDataLength = ; ch ...

  7. 【九度OJ】题目1475:IP数据包解析 解题报告

    [九度OJ]题目1475:IP数据包解析 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1475 题目描述: 我们都学习过计算机网络, ...

  8. Wireshark-过滤器-数据包解析

    目录 过滤器 数据包解析 参考 推荐阅读: https://www.cnblogs.com/zwtblog/tag/计算机网络/ 过滤器 显示过滤器 和 捕获过滤器,俩者使用非常类似. 在Wiresh ...

  9. BLE广播数据的抓包解析

    前言: 报文由数据字节组成同时是按比特传输的,这就免不了牵涉到字节序的问题. 对于各个字节的传输,总是从最低位开始传输.如0x80是按00000001发送的,0x01是按10000000发送的. 同时 ...

  10. BLE 广播数据解析

    从上一篇GATT Profile 简介中提到过,BLE 设备工作的第一步就是向外广播数据.广播数据中带有设备相关的信息.本文主要说一下 BLE 的广播中的数据的规范以及广播包的解析. 广播模式 BLE ...

随机推荐

  1. vue如何获取动态添加的类

    动态添加的类.你在声明周期中的mounted中是拿不到的. 是有在updata这个声明周期中才可以拿到的. 因为此时数据才跟新完成

  2. 【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:加速乐加密 ...

  3. ClickHouse(06)ClickHouse建表语句DDL详细解析

    目录 当前服务器上创建表(单节点) 语法形式 使用显式架构 从相同结构的表复制创建 从表函数创建 从选择查询创建 分布式集群创建表 临时表 分区表 创建表语句关键字解析 空值或非空修饰符 默认值表达式 ...

  4. 2020美亚个人赛wp

    案例背景 2020年9月,数名信用卡持有人向警方报案,指他们的信用卡被不知名人士在一家本地网上商店购买手机.订单大部分来自海外的网络地址,但有一宗订单来自本地.警方经调查后发现该本地网络地址的注册地址 ...

  5. Go实现网络代理

    使用 Go 语言开发网络代理服务可以通过以下步骤完成.这里,我们将使用 golang.org/x/net/proxy 包来创建一个简单的 SOCKS5 代理服务作为示例. 步骤 1. 安装 golan ...

  6. 正则表达式,JSON.stringify() 去除 所有 key的下划线!!一句顶很多句。

    最终的结论: JSON.stringify(userInfo).replace(/([{,]\")_(\w*\":)/g, "$1$2"); 开头锁定<以 ...

  7. clickhouse导入和导出

    一.连接clickhouse--客户端连接default库clickhouse-client -h localhost --port 9001 -u default --password 123456 ...

  8. Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi

    目录 简介 Asp .Net Core 集成 Panda.DynamicWebApi 配置 原理 什么是POCO Controller? POCO控制器原理 ControllerFeatureProv ...

  9. Power BI 13 DAY

    分组依据 在PQ下对维度进行汇总,并对其他字段进行不同的计算方式,就需要使用分组依据 将下表中已"店名"为维度对重复店名作排重处理,对"单号"字段进行计数计算, ...

  10. .NET 9 首个预览版发布:瞄准云原生和智能应用开发

    前言 前不久.NET团队发布了.NET 9 的首个预览版,并且分享.NET团队对 .NET 9 的初步愿景,该愿景将于今年年底在 .NET Conf 2024 上发布.其中最重要的关注领域是:云原生和 ...