一 介绍
ATT全称是Attribute protocol(数据交互协议),这一层的关键词是Attribute(属性)。一个属性其实就是一条数据,属性是BLE数据提供单元,也是蓝牙空中传播数据的最上层,BLE开发过程中接触最多的就是这一层。
属性协议允许称为服务器的设备向称为客户端的对等设备公开一组属性及其相关值。服务器公开的这些属性可以被客户端发现、读取和写入,并且 可以由服务器指示和通知。
属性是一个离散值,它具有以下三个特征:
- 属性类型,通过UUID表示
属性类型指定属性代表什么。 - 属性句柄
属性句柄在服务器上唯一标识一个属性,允许客户端在读或写请求中引用该属性; 客户端使用句柄来识别是哪个属性发送过来的通知或者指示, 客户端能够发现服务器属性的句柄; - 属性权限
由每个使用该属性的更高层次规范定义的一组权限;不能使用Attribute协议访问这些权限。
权限可以应用于属性以防止应用程序获取或更改属性的值。 属性可以由更高层规范定义为可读或可写或两者兼有,并且可能具有额外的安全要求。
通过为每个服务分配不同的句柄范围,可以在单个服务器上公开多个服务。 这些句柄范围的发现由更高层规范定义。
属性也可以被理解成一个被编址并打上标签的一小块数据。每个属性均包含一个用来标识该属性的唯一的句柄、一个用于标识存储数据的类型以及一个值。例如,一个类型是"温度"、值为20.5℃的属性可能放在句柄为0x01CE的属性里。属性协议没有定义任何属性类型,但规定某些属性可以分组,并且可以通过属性协议发现分组的语义。
二 ATT的数据结构
注意:这里说的数据结构不是在空中传播的数据结构,而是一条属性在存储器中定义的数据结构。
2.1 属性类型
每个属性都有一个类型,用UUID表示。例如,温度、压强、体积、距离、功率、时间等都是一个类型,可以使用不同的UUID来表示。任何人都可以创建UUID并按照需求分发或者暴露个client。uuid没有中央注册中心,因为它们基于一个不重复的唯一标识符。
UUID是一个16字节的数据,设备间为了是被数据类型需要发送长达16字节的数据。为了提高传输效率,SIG定义了一个称为蓝牙UUID基数的128位通用唯一识别码,结合一个较短的16位数使用。二者任然遵循通用唯一识别码的分配规则,只不过设备间传输常用UUID时,只发送较短的16位版本,接收方收到后补上蓝牙UUID基数即可。蓝牙UUID基数为: 0x0000xxxx-0000-1000-8000-00805F9B34FB
例如要发送16位识别码为0x2A01,完整128位的UUID为:00002A01-0000-1000-8000-00805F9B34FB。
对于16位的UUID,通常不直接使用其数值,而是冠以一个名称加上书名号。例如用《Include》来表示数值为0x2802的UUID。类似这样的名称定义还有很多。UUID并没有定义自身的用法,为了增加人工调试时的可读性。
Attribute type一般是由service和characteristic规格来定义,站在蓝牙协议栈角度来看,ATT层定义了一个通信的基本框架,数据的基本结构,以及通信的指令,而GATT层就是定义service和characteristic,GATT层用来赋予每个数据一个具体的内涵,让数据变得有结构和意义。换句话说,没有GATT层,低功耗蓝牙也可以通信起来,但会产生兼容性问题以及通信的低效率。
16位UUID的定义参看这里: https://www.bluetooth.com/specifications/assigned-numbers/#assignedNumbers
2.2 属性句柄
属性句柄是一个 16 位值,由每个服务器分配给它自己的属性,以允许客户端引用这些属性。 任何给定服务器上的属性句柄都应具有唯一的非零值。 属性按属性句柄排序。
比如,温度传感器可以有温度属性,设备名称属性和电池电量属性。属性句柄在同一台服务器上是唯一的。
值为 0x0000 的属性句柄保留供将来使用。 值为 0xFFFF 的属性句柄称为最大属性句柄。
2.3 属性句柄分组
分组是由一个特定属性定义的,该属性位于与该属性分组的其他属性范围的开头,这些属性由更高的层规范定义。客户端可以请求与一组属性关联的第一个句柄和最后一个句柄。
2.4 属性值
属性值是一个字节数组,可以是固定长度或可变长度。 例如,它可以是一个字节的值,或4个字节整数,或可变长度的字符串。 一个属性可能包含一个太大而无法在单个 PDU 中传输的值,但是可以使用多个 PDU 发送。
属性值用于表示设备公开的状态信息。属性值对于ATT协议来说并不重要,但他对于上层,包括GATT和GAP来说有着相当重要的意义。
属性类型
用于识别属性类型的UUID包含了一些最基本的属性类型,一般用于GATT规范,而非具体的服务,这些属性类型包括:
- 首要服务
- 次要服务
- 包含
- 特性
特性描述符
一些服务公开的数据包含了额外的信息。这种额外的信息被称为特性描述符。例如一个描述相关数值格式的信息(单位与表示方式)便是所谓的描述符。
2.5 权限
Attribute的权限属性,权限属性不会直接在空口包中体现,而是隐含在ATT命令的操作结果中。假设一个attribute read 属性设为open(即读操作不需要任何权限),那么client去读这个attribute时server将直接返回attribute的值;如果这个attribute read属性设为authentication(即需要配对才能访问),如果client没有与server配对而直接去访问这个attribute,那么server会返回一个错误码:告诉client你的权限不够,此时client会对server发起配对请求,以满足这个attribute的读属性要求,从而在第二次读操作时server将把相应的数据返回给client。目前主要有如下四种权限属性:
- Open,直接可以读或者写
- No Access,禁止读或者写
- Authentication,需要配对才能读或者写,由于配对有多种类型,因此authentication又衍生多种子类型,比如带不带MITM,有没有LESC
- Authorization,跟open一样,不过server返回attribute的值之前需要应用先授权,也就是说应用可以在回调函数里面去修改读或者写的原始值。
- Signed,签名后才能读或者写,这个用得比较少。
2.6 属性访问方法
访问属性方法可分为
- 命令,客户端发送给服务端,不要求服务端回复
- 请求,客户端发送给服务端,要求服务端回复
- 响应,客户端发送了一个请求,服务端用此响应
- 通知,服务端发给客户端的数据,不要求客户端回复
- 指示,服务端发给客户端的数据,要求客户端回复
- 确认,服务端发给客户端的数据,客户端用此进行回复
一些属性协议 PDU 还可以包括一个认证签名,以允许在不需要加密的情况下对该 PDU 的发起者进行认证。
2.7 交换MTU
ATT_MTU 定义为客户端和服务器之间发送的任何数据包的最大大小。更高层规范定义了默认的 ATT_MTU 值。
客户端可以使用 ATT_EXCHANGE_MTU_REQ请求改变ATT_MTU, 服务端使用ATT_EXCHANGE_MTU_RSP PDU 进行回复。
2.8 长属性
可以在单个数据包中发送的最大长度为 (ATT_MTU-1) 个字节。 因为属性操作码至少占用一个字节的PDU长度。属性值的长度可以定义为大于ATT_MTU - 1, 这些属性称为长属性。
使用ATT_READ_BLOB_REQ PDU可以一次性大于(ATT_MTU-1) 个字节的属性值。可以使用ATT_READ_REQ PDU读取长属性值的第一个(ATT_MTU-1)字节。
如果属性值得长度大于(ATT_MTU-3),可以使用ATT_PREPARE_WRITE_REQ PDU和 ATT_EXECUTE_WRITE_REQ PDU一次性写入。可以使用ATT_WRITE_CMD PDU写入长属性值的第一个 (ATT_MTU-3) 字节 。
ATT协议没有规定属性值得最大长度。更高层的协议规定了这个值。最长的属性值不应该超过512字节。
2.9 数据库、服务器和客户端
一组属性的集合被称作数据库。数据库可以很小,小至仅包含6种属性,也可以很大很复杂。最小的属性数据库必须包含以下6种属性:
- 《GAP服务》的《首要服务》
- 《设备名》的《特性》
- 《设备名》的值
- 《外观》的《特性》
- 《外观》的值
- 《GATT服务》的《首要服务》
属性数据库总是位于属性服务器当中;通过属性协议、属性客户端和属性服务器进行通信。不管低功耗蓝牙还是经典蓝牙设备,每台设备最多只有一个属性服务器和一个属性数据库。对低功耗蓝牙设备而言,由于强制要求属性数据库支持通用访问规范(GAP)服务,每一个低功耗蓝牙设备都要包含一个属性服务器和一个属性数据库。由于强制要求有一个属性服务器和一个属性数据库,要加入一个电量指示服务,只需要向当中追加3种或者更多属性。
三 ATT层空中传播的数据格式
- Opcode: 操作码,用来表示需要执行的操作类型。下图列出来ATT层所支持的Opcode,详见: Version 5.1 | Vol 3, Part F
- Parameters:操作类型相应的参数
- Signature:是否需要认证许可
ATT层将构建好的数据发送给L2CAP层进行传输:
四 抓包分析ATT层的数据
发现GAP服务
1 Read by Group Type Request
方向: M ——> S
0d 03 40 04 00 10 09 0d 55 c9 c3 ca 2b 0e 0b 07 00 04 00 10 01 00 ff ff 00 28 c0 4e 24
-
LL preamble: 55
-
LL Access Address: c9 c3 ca 2b
-
LL Header: 0e == 0000 1110
-
LL length: 0b
-
L2CAP length: 07 00
-
L2CAP CID: 04 00
-
ATT Attribute Opcode: 10
-
ATT Starting Handle: 01 00
-
ATT Ending Handle: ff ff
-
ATT Attribute Group Type: 00 28
-
ATT CRC: c0 4e 24
LL层
- Preamble(1Byte): 0x55
- Access Address(4Byte): 0x2bcac3c9
- header(1Byte)
- LLID(2 bits): 10b
- MSN(1 bit): 1b
- SN(1 bit): 1b
- MD(1 bit): 0b
- RFU(3 bits): 000b
- length(1Byte):0x0b
L2CAP层
- length(2 Byte): 0x0007
- CID(2 Byte): 0x0004
ATT层
- Attribute Opcode: 0x10
- Starting Handle: 0x0001
- Ending Handle: 0xffff
- Attribute Group Type: 0x2800 详见 Vol 3, Part G, 3.4, Table 3.18
- CRCc0: 0xc04e24
2 Read by Group Type Response
方向: S ----> M
0d 03 0c 06 00 20 0d 14 55 c9 c3 ca 2b 06 12 0e 00 04 00 11 06 01 00 09 00 00 18 0a 00 0a 00 01 18 b1 18 63
-
LL preamble: 55
-
LL Access Address: c9 c3 ca 2b
-
LL Header: 06
-
LL length: 12
-
L2CAP length: 0e 00
-
L2CAP CID: 04 00
-
ATT Attribute Opcode: 11
-
Length: 06
-
Attribute Data List: 01 00 09 00 00 18 0a 00 0a 00 01 18
-
CRC: b1 18 63
LL层
- Preamble(1Byte): 0x55
- Access Address(4Byte): 0x2bcac3c9
- header(1Byte)
- LLID(2 bits): 10b
- MSN(1 bit): 1b
- SN(1 bit): 0b
- MD(1 bit): 0b
- RFU(3 bits): 000b
- length(1Byte): 0x12
L2CAP层
- length(2 Byte): 0x000e
- CID(2 Byte): 0x0004
ATT层
- ATT Attribute Opcode: 11
- Length: 06 (The size of each Attribute Data)
- Attribute Data List:
- group 1(6Byte):
- starting Attribute handle: 0x0001
- ending Attribute handle: 0x0009
- Attribute Value: 0x1800 (GAP服务, 详见 Vol 3, Part G, Table B.1 )
- group 2(6Byte):
- starting Attribute handle: 0x000a
- ending Attribute handle: 0x000a
- Attribute Value: 0x1801 (GATT服务, 详见 Vol 3, Part G, Table B.1 )
- group 1(6Byte):
- CRC: 0xb11863