ble- ATT profile详解

闲话

  • 熟视无睹的东西,更需要更加深入的研究,ATT作为最基本的ble常识,很多时候并没有深刻的总结
  • 专精自己熟悉的领域,其次了解更多周边领域,要足够体现自己的专业性,而不是浮于表面
  • 沟通的前提,是要思路清晰,明白对方的意图,有待加强

从数据格式了解ATT

在这里插入图片描述

Opcode

  • 总体分为6种大类型的Opcode

request / response
indicate / confirm
command
notify

  • 所有的ATT数据,都是属于这6种类型的子类
  • request 和command从client 端发起,区别是request 必须要有response ,而command 不需要,单向
  • indicate和notify从server 端发起,区别是indicate必须要有confirm 返回,而notify不需要,单向
  • opcode 占用1个字节

ATT 参数

att 参数主要分为三个部分,但是并不是所有ATT 数据包都需要全部包含这三部分内容

- ATT TYPE
由uuid 决定,uuid有16bit ,32bit 和128bit long uuid几种格式 ,一些常用的uuid,比如primary service,characteristic 等等由SIG 官方发布,可以查表。用户也可以自定义uuid

32bit uuid在PDU发送的时候,需要转成128bit发送,转换公式如下:
128_bit_value = 16_bit_value * 296 + Bluetooth_Base_UUID
128_bit_value = 32_bit_value * 296 + Bluetooth_Base_UUID

16bit uuid 在发送的时候,没有这个要求,ATT解析uuid 支持16bit 和128bit 两种格式

- ATT handle
所有ATT数据都是通过handle 去访问,handle 类似于数组的index,每个att 数据都有唯一的handle去与之对应,
handle 是一个1-0xFFFF直接的数据,每个handle依次增加

- ATT value
ATT 数据值是根据ATT type决定存储的格式,可以是一个字符串或者一个handle值。
根据该条ATT type,也可以解析其数据
数据的整体空间可以为定长,也可以为变化长度

数据加密

  • 可以通过设置opcode bit7,需要加密的ATT数据,将会占有12个字节的空间
  • 如果有打开,加密信息会附加到ATT PDU末尾,对端设备需要有key 才能读取该条ATT数据
  • ATT数据的实际使用空间为 未加密:ATT_MTU-1 加密:ATT_MTU-13

从opcode了解ATT操作流程

在这里插入图片描述

0x01 ERROR_RSP

  • 报错的opcode,handle,是什么错误(error code,1个字节)
  • 经常error rsp用于表示流程结束标志
  • 具体的error code ,此处不贴图,可以查阅spec
  • 所占字节数1+1+2+1

0x02 EXCHANGE_MTU_REQ/ 0x03 EXCHANGE_MTU_RSP

  • request ,client向server发起交换mtu 的请求,request中携带自己支持的mtu值(2字节)
  • server 端收到之后,会根据自己所支持的mtu值与接收到的比较,取较小的值,回复给client端
  • 在没有收到exchange rsp 之前,双方用默认值ATT_MTU=23
  • ATT_MTU大小范围在23-512之间
  • 如果ATT 链接建立过程中没有MTU request ,双方使用默认值

0x04 FIND_INFORMATION_REQ /0x05 FIND_INFORMATION_RSP

  • request,client 发给server,大海捞针。
  • client 不知道server有定义哪些服务,告诉一个区间,返回server 定义的ATT类型
  • att支持的类型是有uuid 决定的,uuid有两种,16bit,128bit,response 中format 决定
  • 返回的信息需要告诉handle 和uuid 成组的信息,handle-uuid成对
  • requset 格式: opcode(1)+start handle(2)+end handle(2)
  • response格式:opcode(1)+ format(1)+ [ handle- uudi ] +[handle -uuid]
  • 一般用于discover具体charteritic 的值,搜索范围会很小,精确读取未知handle的att 类型

0x06 FIND_BY_TYPE_VALUE_REQ/0x07 FIND_BY_TYPE_VALUE_RSP

  • 另外一种大海捞针的查找方式,较于find information request,多一个uuid和value 限制条件
  • start ,end 定义一个查找区间
  • 要查找什么类型,设置uuid过滤条件,比如primary service
  • att value,设置att value过滤条件,比如0x1801,定义GAP service值
  • 一般常用于discover service 开头,将范围设置为0-0xffff,返回GAP祥光的primary service,过滤条件能够减少返回的数量
  • 0x04,0x05,0x06,x07主要用于初次获取对端ATT 的类型,返回值都是type类型。拿到对端的类型之后,才能通过恰当的方式去操作ATT,读取ATT携带的数据

0x08 READ_BY_TYPE_REQ/ 0x09 READ_BY_TYPE_RSP

  • 需要知道server段关于目标类型的ATT 数据分布情况
  • 常见的UUID type 包括:include,characteristic,PNP ID等等
  • 此类UUID一般是广泛的搜索,宏观的定义
  • 返回信息 handle+value,返回信息value是搜索类型的申明ATT信息,handle是指搜索类型对应的handle。
  • 所有定义的类型,第一条ATT就是申明
    在这里插入图片描述
    以一个栗子说明,RSP中的数据结构
    handle = 50, 该characteristic 申明对应的handle
    value :包含三部分:
    properties属性,包括这个特征的操作方式,每个characteristic中都包含这个部分
    value handle, 指定这个charteristic实际值保存的位置,可以被操作的对象
    characteristic uuid,指定这个可操作值value 的类型,可以理解为characteristic的子类
    特征申明handle一般和实际value handle相邻,discover 的最终目的是通过申明找到操作value的方法,
    然后通过数据所在的handle去操作

0x0A READ_REQ / 0x0B READ_RSP

  • 通过read by type获取到了操作方法及value对应的handle 值,下一步就是直接去获取handle对应的value

  • 在读这个handle之前,必须要先获取到对端的类型,只有这样,返回的值才能够被正确解析

  • SIG有规定部分类型的值 的数据格式,用户也可以自定义数据类型及其对应值的格式

  • reuqust: opcode(1)+handle(2)

  • response:opcode(1)+value(最长为mtu-1)

0x0C READ_BLOB_REQ / 0x0D READ_BLOB_RSP

  • 如果ATT携带数据超过了MTU最大值,第一次Read 肯定读取不完,这种情况下需要使用多次读取。

  • 如何判断数据超长,一次没有读取完? 第一次read response中,数据 size 等于(最大值-1),则马上改用 read
    blob req读取余下的所有值, 直到response中 数据size 小于(最大值-1)

  • 读取的过程中,request 会传递offest=(最大值-1)的计数,保证数据完整性。每读取一次offset累加(最大值-1)

  • 因此read blob request一般都在第一次read request 之后用

  • request:opcode(1)+handle(2)+offset(2)

  • response:opcode(1)+value(最长为mtu-1)

0x0e READ_MULTIPLE_REQ / 0x0f READ_MULTIPLE_RSP

  • 如果遇到多个value size较小,多次读取显然不是一个明智的选择,
  • read mutil request能够实现一次读取多个value值
  • 但是有一些风险需要注意:
    1. 任何一个handle,单独read 有error,整个rsp都会fail,如: Insufficient Authorization,Insufficient Encryption Key Size.
    2. 必须要保证每个value size在上层都是已知且固定的,并且总大小不超过mtu-1,方便解析接收的数据
    3. 并不要求每个value size一致
    4. value总大小 = mtu-1,避免使用该命令,无法判断数据有全部读完
    5. 如果value 列表总长超过了mtu-1, 只会返回第一个value值

0x10 READ_BY_GROUP_TYPE_REQ / 0x11 READ_BY_GROUP_TYPE_RSP

  • ATT group 是一个虚拟的概念,并没有指定的数据,明确表示具体的ATT属于哪一个service group

  • ATT数据结构中最大的单元是service。以service 为例, 每个service,以service declaration开头,以下一个service declaration结束,在这两个handle直接就属于同一个service group
    以GATT数据框架为例说明:在这里插入图片描述
    每一个矩形框就是一个ATT数据,不同的ATT数据根据类型分布组成各个不同group

  • rsp 返回的是改group 的start handle 和end handle

  • 并没有特殊的group type 定义,一般搜索的也是宏观的uuid,例如:primary service 0x0028

  • req 中指定 搜索范围, group type 类型
    opcode(1)+start handle(2)+end handle(2)+uuid(2或16) 0x0028

  • resp 如果总长超过mtu-1,只会第一组数据,每组数据格式如下:start handle(2)+end handle(2)+uuid(2或16)0x180f
    返回的uuid 是服务声明的类型,具体的service 类型,例如:电量服务0x180f

  • 重点理解这两个uuid 的含义:
    1.primary service 0x0028 服务类型,具体信息:电量服务0x180f
    2.一个服务的声明,是一个ATT数据,包括一个handle,type(电量服务0x180f),服务类型(0x2800/0x2801)

0x12 WRITE_REQ / 0x13 WRITE_RSP

  • 用于向已知handle 写入值
  • 如果写成功,会有write RSP返回
  • write 用于已经知道所操作handle 的数据类型,get db 结束之后
  • opcode(1)+handle(2)+value(最多mtu-3)

0x52 WRITE_CMD / 0xD2 SIGNED_WRITE_CMD

  • 用于client向 service写入数据,单向写入,没有反馈
  • 典型的 点控ATT,control-point attribute
  • 两者的区别,后者携带加密信息,opcode bit7 置 1,所以可携带的数据大小最多为(mtu-15)

0x16 PREPARE_WRITE_REQ/ 0x17 PREPARE_WRITE_RSP

  • 针对超长ATT value的操作,可以分批次多次写入
  • req 中写的写入目标handle ,offset,value
  • rsp 中携带已经完成写入的handle ,offset
  • 如果req 和rsp 中handle 和offset相同,则表明写入成功
  • 这个命令执行完必须搭配下一个命令,才能生效,server端仅仅只是cache这个value,还没有写入。

0x18 EXECUTE_WRITE_REQ / 0x19 EXECUTE_WRITE_RSP

  • 搭配上一个命令使用,包含两种状态,生效或者取消
  • server接受到这个命令后,会根据状态,执行对应的操作,写入或者取消
  • 在server 端,这个命令是原子操作,不可以被中断

0x1b HANDLE_VALUE_NTF

  • server主动发起,notify 可以在任何时候发出,不需要client 回复
  • 一个notify中携带 opcode(1)+handle(2)+value(最多mtu-3)
  • 常见的hid device 都是使用这种方式发送键值信息

0x1d HANDLE_VALUE_IND / 0x1e HANDLE_VALUE_CFM

  • 又一种主动从server 端发起的ATT类型
  • 一个indication携带的数据 opcode(1)+handle(2)+value(最多mtu-3)
  • 与notify最大的区别是,需要client回复comfirm,如果回复超时,ACL 对断开
  • 如果client 接收到indication,会回复confirm,携带opcode(1)
  • 一次只能发送一个indication,前一个indication对应的confirm没有拿到之前,不会发送下一个,处于block状态

0x23 MULTIPLE_HANDLE_VALUE_NTF

  • 一个notify 中可以包含多个handle value值
  • 一组数据中需要携带每一个handle value 长度值,告诉client 如何分割数据
  • 如果对应的handle 无效,client 会忽略该值
  • 任何时候server都可以发送该notify,不需要client回复
  • length 长度为2个字节
  • 一组数据的格式为length(2)+ handle(2)+value(length-4)

安全相关

  1. ATT 的访问时建立在一条已经加密过的acl 之上
  2. 加密的相关流程在GAP中完成
  3. 在air log中,输入link key,就能够解析出ATT层的具体数据信息
  4. 每条ATT可以设置独立的安全机制,可以设置被写或者被读
  5. 当client端操作一个ATT之前,server端需要先检查其具备的权限
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章