Bluetooth Attribute Protocol Spec 解读

什么是Attribute protocol

    简单来说,这个协议就是用来给Server和Client进行通信的协议。Server端保存有一个类似“属性数据库”的东西,包含了一系列的属性及其特性。而Client端可以通过ATT协议从Server端获取这些属性。再具体一些,Client可以查询(Discover)、读取(read)甚至配置(write)Server中保存的属性。通常是在配置之后,Server端可以实时的告知Client端属性值的变化。通知可以是无需Client应答的(notification),也可以是需要Client响应的(indication)。


Server端的Attribute

    server端保存了一系列的attribute,这些attribute由四个基本元素组成:

  • attribute handle
  • attribute type
  • attribute value
  • attribute permissions

    Attribute handle其实就是一个属性在server端的索引。它对每个属性来说都是唯一的,不可有重复的值,否则Client端无法准确定位到某一个属性。Handle的范围是 [0x0001, 0xFFFF)。

    Attribute type是属性的类型,ATT中使用一个(16/128bits)的UUID来表示。虽然概念是属于ATT协议,但ATT本身 并没有声明具体的UUID来作为某一个attribute type。这意味着,ATT只是提供了一个类似框架的东西,具体内容怎么填,还是由上层来定义。比如GATT(Generic Attribute Profile)就定义了几个基本的attribute type:Primary Service(UUID为0x2800)、Characteristic(UUID为0x2803)。

    Attribute value是一个属性的值,不过这里仅仅是ATT这一层的value,上层协议可以对其进行更具体的划分。还是以GATT为例,一个Characteristic的attribute value就由property、value handle和UUID三部分组成。

    Bluetooth Attribute Protocol Spec 解读

    最后一个元素attribute permissions,它描述了一个属性的访问权限,包括

  • read
  • write
  • encryption
  • authentication
  • authorization

    比较特殊的是,这些权限由上层定义,对于ATT来说却是不可见的,Client无法获取到Server中属性的permissions。

    以上结构,可以Generic Attribute Profile(GATT Profile)当中的图来诠释:

        Bluetooth Attribute Protocol Spec 解读

    总的来说,Server端保存的attribute大致就像下面的表格所描述的那样:

        Bluetooth Attribute Protocol Spec 解读


几个特殊的概念

Group——Attribute的分组

        如果有一组attribute,他们描述的是一个整体的不同部分,那么就可以将他们归为一组。实现这种功能的方法,就是在这组attribute的开头单独定义一个attribute,由它来说明这组属性的起止handle以及他们所共同描述的对象是什么。这里先借用下后面会提到的一个ATT协议的PDU——Read by group type response,它包含的就是Group开头的这个attribute的value:

    Bluetooth Attribute Protocol Spec 解读

    以上,Attirbute Handle就是这组Group的起始Handle,End Group Handle就是他的最后一个attribute的handle,Attribute Value通常是一个上层定义的UUID,比如HID Service的UUID(0x1812)。下面就是一个具体的Read by group type response的HCI层视图,虽然它已经跨越了GATT,但不影响对Group的理解:

    Bluetooth Attribute Protocol Spec 解读


Control-Point Attributes

    这是一类只能配置(write)而不能读的属性,因此它通常是提供给Client来配置Server用的。比如在HID Service当中,HOGP host可以通过HID Control Point来通知HOGP device进入或退出suspend状态。


Exchange MTU Size

    故名思议,它描述的是一个改变ATT协议MTU size的动作。Default MTU size for ATT protocol是23个字节,但是它是可以由Client、Server双方协调来变大的。具体的过程就是,Client将本地的最大RX MTU告诉Server,反过来Server也将自己最大的RX MTU告知Client;双方选择两者中较小的一个,作为双向通信的MTU size。

    具体在使用时,变大MTU size可以减少信息交互的时间。以HOGP中的Report Map为例,原本可能需要好几个LE connection event来完成一个Report Map的传输(主要原因是需要应用的参与,来不及在一个LE connection event内传输多个Request请求),更新MTU size之后,可以直接在一个LEconnection event中完成(上层直接将一个大号的packet送给hardware,不需要了解下面fragment的细节)。


Long Attribute Value

   ATT本身是没有定义packet length这个概念的,只能通过L2CAP来推测其长度。有些属性的value短,可以直接放在一个ATT PDU中;有些则长一些,需要多个分多个ATT PDU传输。ATT专门定义了long attribute value的处理,允许Client以offset的方式,将一个attribute value分多次从Server端取出。

    当然,ATT也对value的长度做出了限制,最大512 bytes。要是还不够,那该怎么办呢?目前看到HOGP里面有一种解决方案。如果一个HOGP device的Report Map长度超过了512 bytes,那么device端可以声明两个HID Service,将Report Map分到两个HID Service的定义中去。


Attribute PDU

    ATT定于了六种类型的PDUs,包括Command、Request与Response、Notification、Indication与Comfirmation。总的来说,他们都具有下面的PDU format:

    Bluetooth Attribute Protocol Spec 解读

Name Size(Octets) Description
Attribute Opcode 1 The attribute PDU operation code
bit7: Authentication Signature Flag
bit6: Command Flag
bit5-0: Method
Attribute Parameters 0 to (ATT_MTU - X) The attribute PDU parameters
X = 1 if Authentication Signature Flag of the Attribute Opcode is 0
X = 13 if Authentication Signature Flag of the Attribute Opcode is 1
Authentication
Signature
0 or 12 Optional authentication signature for the Attribute Opcode and Attribute Parameters

    Opcode好理解,就是操作码。它有两个flag,而且两个都是给(Signed)Write Command来使用的(是不是有点浪费)。    

    Command flag表明这是个一个command,只有Write Command和Signed Write Command会将这个flag设置为1,其他Opcode的此标志位都为0。  

    Authorization Signature Flag就只有Write Command会用到了。它表示是否需要Authorization Signature,后者用于对数据进行认证。只有在非加密(encrypted)链路上才可以使用,毕竟两者都是为了对数据加密,没有必要两者都用。

    除了Attribute Value之外,其他ATT PDU中的multi-octet字段都是使用小端字节序进行传递。Attribute Value本身保存了上层协议定义的值,因此它的字节序由高层协议来定义。


    从功能来看,ATT PDU分为四类,分别是:

  1. 交换MTU size的Exchange MTU Request/Response
  2. 查询attribute handle与attribute type映射关系的Find ***系列PDUs
  3. 获取attribute value的Read ***系列PDUs;
  4. 配置attribute的Write ***系列PDUs;
  5. Server端用于通知value变化的Notification、Indication以及Client端的响应Comfirmation

    具体各个PDU的格式,不再一一详述,可以参见Spec最后列出的Table 3.37


相对复杂、需要注意的PDU

Find By Type Value Request/Response

    从格式上看,“Find By Type Value Request”是根据Type与Value来定位handle。然而,“Find By Type Value Response”的内容却是一个个“Found Attribute Handle -- Group End Handle”组成的列表信息。实际上,这里的Group End Handle只是字面上的一种情况。如果是Group,Rsp就返回真实的Group End Handle;而如果不是,则这里的“Group End Handle”将和“Found Attribute Handle”保持一致。


Read Blob Request/Response

   注意到这样一个问题:ATT不包含PDU的length信息。从而,只能通过L2CAP的长度信息来确认。对于Read Blob Request来说,得确定一个attribute的完整value还没有读完,才应该继续使用这个PDU来读取。这里,ATT使用一种推测的方式,来判断是否已经获得了一个attribute的完整value。如果Rsp中的PDU长度等于MTU size,则很有可能还没有读完,需要继续;否则,则Client可以认为已经读完整个长度的value。如果刚好某个ATT PDU的长度等于MTU size,而这个attribute value的长度也到此为止了,则Client的下一个Req会收到一个长度为0的Rsp。

    此外还有一种特殊的情况——value的length是可变的。这种情况下Client判断读完的依据也有两种:

  1. Rsp中的长度小于MTU size;
  2. 收到一个error code为《invalid Offset》的Error Response
   此外还有一点,对于一个非“Long Attribute Value”使用Read Blob Request会导致失败。因此,正确的流程应该是首先使用“Read Request”,如有更多数据则再使用“Read Blob Request”。

Read Multiple Request/Response

   有些Response的PDU会声明它所包含的每个数据项(Attribute Data)的长度,比如“Read By Type Response”和“Read By Group Type Response”;而“Read Multiple Response”就不会包含长度信息。因此使用这两个PDU的前提是,Client必须知道请求的每个attribute的value的长度。当然,这些长度都是固定的,不是可变的。


超长的Notification与Indication

    这个规则给我的感觉就很另类了。ATT协议描述的是,如果一个Notification或者Indication的PDU length等于MTU size,说明它可能还有更多的数据,当前只是一部分。需要Client再调用“Read Blob Request”来获取剩下的value。

    实现上,建议还是确保MTU size要大于任何attribute value的值,否则效率太低了(来回用到至少三个interval才能完成)。


Security考虑

   每个attribute的permission都是由具体每个上层协议决定的,Server在收到Client的请求时应该对读写、加密、认证、鉴权等进行检查。不过对于“Find Information Request”这样的请求,Client仅仅是查询attribute handle与attribute type的映射关系,并不涉及到敏感的信息(attribute value)。因此,ATT协议规定,任何情况下Server端都应该正常响应“Find Information Request”。