蓝牙配对过程分析(四)_java

 

一位平凡、码农姑娘的闲言碎语

如果有些问题让你感觉到很难

别着急,列出已知条件,拆解细分

总能解决

前几篇文章蓝牙配对过程分析(二)--发起配对应用层篇介绍了应用层发起配对的过程,一张图来总结下上文的流程

 

蓝牙配对过程分析(四)_java_02

 

可以看到从AdapterService一路向下,介绍到了btm_sec文件,看起来还算简单

上一篇蓝牙配对分析(三)--协议栈流程介绍了配对过程中整个协议栈要完成的hci命令和事件

接下来继续分析,看一下传统蓝牙的协议栈配对流程

 

蓝牙配对过程分析(四)_java_03

 

流程很多,又想尽可能详细,所以对流程图进行了拆分,上图只是一部分,接下来的流程图还有很多Security方面的操作

文章都很简单,属于入门篇,围绕着流程图的调用流程分析

所以先看下流程图,熟悉的小伙伴可以跳过

从流程图上看到hcicmds负责组装command,btu_task循环监听来自controller的event并在btu_hcif中进行处理

紧接着上文,在BTIF线程中进行配对初始化

 1//system/bt/btif/src/btif_dm.cc
2static void btif_dm_cb_create_bond(const RawAddress& bd_addr,
3                                   tBTA_TRANSPORT transport) {
4  //是否属于hid设备
5  bool is_hid = check_cod(&bd_addr, COD_HID_POINTING);
6  //配对状态回调给jni
7  bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
8  ...
9  if (transport == BT_TRANSPORT_LE) {
10    /** 这里用到了transport
11     * 根据上文应用层分析 我们传入的是transport_auto
12    * 暂时不关注该操作
13    */
14  ...
15 }
16 if ((btif_config_get_int(bdstr, "DevType", &device_type) &&
17      (btif_storage_get_remote_addr_type(&bd_addr, &addr_type) ==
18       BT_STATUS_SUCCESS) &&
19      (device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) ||
20     (transport == BT_TRANSPORT_LE)) {
21      //针对ble设备的配对方式
22      ...
23 }
24
25 if (is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0) {
26   ...
27   //针对hid设备的特殊处理
28 } else {
29   //排除上述场景后的通用配对方式
30   BTA_DmBond(bd_addr, addr_type, transport);
31 }
32 //设定标志位
33 pairing_cb.is_local_initiated = true;
34}

该方法首先进行配对状态回调,应用层的配对状态从BT_BOND_STATE_NONE修改为BT_BOND_STATE_BONDING

接着就是针对ble设备,hid设备以及其他情况的分类处理

暂且搁置ble和hid设备的配对方式,先来看看通用情况下的配对过程,即BTA_DmBond

这个方法往下继续走,就是去初始化一个配对流程,配对流程的作用就是在蓝牙链路上启用身份认证,以及可选的加密措施

BTA_DmBond里边儿就一句话,我就不贴了,感兴趣的可以去看这个文件system/bt/bta/dm/bta_dm_api.cc

接下来的调用就是bta_dm_bond

 1//system/bt/bta/dm/bta_dm_act.cc
2/** Bonds with peer device */
3void bta_dm_bond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
4                 tBTA_TRANSPORT transport) {
5  ...    
6  tBTM_STATUS status = BTM_SecBond(bd_addr, addr_type, transport, 0, NULL, 0);
7
8  if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) {
9    ...
10    //配对异常处理
11  }
12}

其实就是一个方法调用,加一个异常处理

暂且先不论配对异常后如何处理,先缕一缕正常逻辑

这就好比你究竟是跟一个瘸子学走路还是跟一个双腿正常的人学走路?
先学会正常方式,再去观察其他情况

注意看,又附加了三个参数,pin码--长度为0,pin--内容为空,trusted_mask指明当前是否是可信任的设备,设定为0即为不信任的设备

先刨个坑儿,后续介绍这三个参数的用法

 1tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
2                        tBT_TRANSPORT transport, uint8_t pin_len,
3                        uint8_t* p_pin, uint32_t trusted_mask[]) {
4  if (bluetooth::shim::is_gd_shim_enabled()) {
5    //Gabeldorsche模式
6    return bluetooth::shim::BTM_SecBond(bd_addr, addr_type, transport, pin_len,
7                                        p_pin, trusted_mask);
8  }
9
10  if (transport == BT_TRANSPORT_INVALID)
11    //
12    transport = BTM_UseLeLink(bd_addr) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
13
14  tBT_DEVICE_TYPE dev_type;
15
16  BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
17  /* LE device, do SMP pairing */
18  if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) ||
19      (transport == BT_TRANSPORT_BR_EDR &&
20       (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) {
21    return BTM_ILLEGAL_ACTION;
22  }
23  return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin,
24                                   trusted_mask);
25}

这里有一个有意思的判断:is_gd_shim_enabled用于判断蓝牙协议栈是否处于gd模式,如果是那就走shim这一套协议栈的处理

gd什么意思?是Gabeldorsche的缩写,这是Android11的一个开发者选项,一种新的蓝牙协议栈模型,小型试用品,google拿来给大家试用的

这名字也是取自于某位国王的后代,所以这就是蓝牙的继承者们

BTM_SecBond方法也很简单

  • 判定是否处于gd模式,若是则走gd协议栈的处理逻辑

  • 接下来指定链路的transport,并读取设备类型信息和地址信息

  • 最后就通过btm_sec_bond_by_transport继续方法调用

gd这个继承者还没长大呢没啥经验,掌不了实权,所以乖乖看掌权的吧

首先要指定transport,上文中我们说到传入的transport参数为auto--0

对应的协议栈的值分别为什么呢?
1#define BT_TRANSPORT_INVALID 0
2#define BT_TRANSPORT_UNKNOWN BT_TRANSPORT_INVALID
3#define BT_TRANSPORT_AUTO BT_TRANSPORT_INVALID
4#define BT_TRANSPORT_BR_EDR 1
5#define BT_TRANSPORT_LE 2
一目了然,auto就对应invalid,那就是说接下来就是要去重新分配
1  transport = BTM_UseLeLink(bd_addr) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;

选择要使用的基础物理链接,如果是用ble物理链路,那么就选le的transport,否则就选BR_EDR的物理链接

这个transport啥用呢?先留个疑问,接下来先看怎么确定物理链路

根据蓝牙设备的设备类型来决定选用哪种transport
蓝牙设备类型共三种

1//system/bt/stack/include/bt_types.h
2//经典蓝牙单模
3 BT_DEVICE_TYPE_BREDR = (1 << 0),
4//ble蓝牙单模
5 BT_DEVICE_TYPE_BLE = (1 << 1),
6//双模
7 BT_DEVICE_TYPE_DUMO = BT_DEVICE_TYPE_BREDR | BT_DEVICE_TYPE_BLE,
蓝牙设备类型就单模双模两大类,同时单模又分经典和ble两种,所以共三种类型,没啥好说的。贴出来代码方便大家自己去追踪transport选取规则便是如果是ble则选择le的transport,若不是le则选择br/edr的transport选定transport后,调用btm_sec_bond_by_transport继续咱们的工作
1//system/bt/stack/btm/btm_sec.cc
2tBTM_STATUS btm_sec_bond_by_transport(const RawAddress& bd_addr,
3                                      tBT_TRANSPORT transport, uint8_t pin_len,
4                                      uint8_t* p_pin) {
5...
6}
该方法传入四个参数,bd_addr为设备地址,transport为上一步中所得到的transport类型,pin_len和p_pin是用在蓝牙2.1版本的,目前已经基本没用了简单说一下就是,蓝牙配对有两种方式,2.1版本之前用的pairing,2.1版本之后用的是SSP(Secure_Simple_Pairing)所以这也是为什么目前Android11源码中p_pin相关的参数根本没对上层api开放,并且协议栈也是传null处理稍微跑题了,接着来继续分析代码流程
 1//system/bt/stack/btm/btm_sec.cc
2tBTM_STATUS btm_sec_bond_by_transport(const RawAddress& bd_addr,
3                                      tBT_TRANSPORT transport, uint8_t pin_len,
4                                      uint8_t* p_pin, uint32_t trusted_mask[]) {
5  ...
6  //查找是否已经存在acl连接链路
7  tACL_CONN* p = btm_bda_to_acl(bd_addr, transport);
8   ...
9  /* Other security process is in progress */
10  if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {
11    //协议栈正在进行配对过程
12    return (BTM_WRONG_MODE);
13  }
14  //在DeviceDb中查找设备记录,若没有,则创建一个新的记录并保存
15  p_dev_rec = btm_find_or_alloc_dev(bd_addr);
16  ...
17  //controller是否已经准备好
18  if (!controller_get_interface()->get_is_ready()) {
19    ...
20    return (BTM_NO_RESOURCES);
21  }
22  ...
23  /* Finished if connection is active and already paired */
24  if (((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) &&
25       transport == BT_TRANSPORT_BR_EDR &&
26       (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) ||
27      ((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) &&
28       transport == BT_TRANSPORT_LE &&
29       (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))) {
30    //连接已激活或者已经配对
31    return (BTM_SUCCESS);
32  }
33  //controller删除linkKey
34  if ((BTM_DeleteStoredLinkKey(&bd_addr, NULL)) != BTM_SUCCESS)
35    return (BTM_NO_RESOURCES);
36
37  if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) {
38    ...//如果有pin码则保存有效的pin码
39  }
40  //修改btm结构体块的参数
41  btm_cb.pairing_bda = bd_addr;
42  btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD;
43  //修改设备记录的一些参数
44  p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;
45  p_dev_rec->is_originator = true;
46  ...
47  if (transport == BT_TRANSPORT_LE) {
48    ...//针对Le的transport单独处理
49    return (BTM_NO_RESOURCES);
50  }
51  //更新设备参记录的一些参数
52  p_dev_rec->sec_flags &=
53      ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED |
54        BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);
55  if (!controller_get_interface()->supports_simple_pairing()) {
56    ...//controller是否支持ssp的配对方式
57  }
58  ...
59  if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE) {
60   ...//连接已经存在的处理
61    return (BTM_CMD_STARTED);
62  }
63  if (!controller_get_interface()->supports_simple_pairing() ||
64      (p_dev_rec->sm4 == BTM_SM4_KNOWN)) {
65  ...//如果任一方不支持ssp的处理
66  }
67  if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
68       btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
69       btm_cb.security_mode == BTM_SEC_MODE_SC) &&
70      BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
71      ...//修改配对状态
72      btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
73      //读取远程设备name
74      status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
75  } 
76  if (status != BTM_CMD_STARTED) {
77    ...//异常处理
78  }
79  return status;
80}
看完这段代码就一个感觉,好复杂...不过还好我还没没被劝退,代码很长,总结一下就是
  • 更新btm的控制块btm_cb参数,全局结构体

  • 更新设备记录信息p_dev_rec参数(SecurityDeviceRecord),和远端设备一一对应

  • 根据当前的不同状态做对应处理

  • 针对支持SSP的设备首先修改状态,之后通过发送hci命令来读取远程设备name

配对过程是需要为要配对的设备添加各种flag并把要配对的设备信息修改存储到协议栈中,即创建p_dev_rec

同样的,修改协议栈中和该远程设备相关的一些字段信息

来看下代码的具体处理,首先获取到和该远程设备相关的acl链接信息

1tACL_CONN* p = btm_bda_to_acl(bd_addr, transport);

针对未进行过配对连接的设备,那么指定是空了

接下来就是当前协议栈是否正在进行配对的状态检测

1 if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {

如果不处于idle也即是当前正在配对过程中,则表明本机蓝牙正在进行和其他设备配对过程,所以不处理新设备的配对请求

接下来就是看一下设备是否已经记录在册了

1p_dev_rec = btm_find_or_alloc_dev(bd_addr);
2  if (p_dev_rec == NULL) {
3    return (BTM_NO_RESOURCES);
4  }

根据远程蓝牙设备地址Address,从Device数据库中查找记录

若未查找到设备记录deviceRecord,就创建一个新的,如果创建失败,则本次配对失败,失败原因为资源不足NO_REOURCES

这个设备记录哪儿来的呢?也是你自己创建的,就是在你创建时会写入数据库中

具体方法调用就是btm_find_or_alloc_dev->btm_sec_alloc_dev->btm_sec_allocate_dev_rec

这个创建时传入的信息要说一下,因为影响到后续配对过程

创建的设备结构体 tBTM_SEC_DEV_REC,结构体内容太多,这里只贴一些创建时会赋值的参数

会在btm_sec_allocate_dev_rec方法中分配新的设备记录资源

在创建时如果超过数量(BTM_SEC_MAX_DEVICE_RECORDS)限制,则删除最早的无用记录

创建后加入到设备数据库中,创建时会初始化一些参数

  • sec_flags:设定初始值BTM_SEC_IN_USE,当前设备的安全状态,包括授权加密等

  • bond_type:设定初始值BOND_TYPE_UNKNOWN,当前设备的配对状态,包括 BOND_TYPE_PERSISTENT持久和BOND_TYPE_TEMPORARY临时两种配对类型

  • timestamp:设定初始值btm_cb.dev_rec_count++,即btm控制块中设备记录的索引号

  • rmt_io_caps:设定初始值BTM_IO_CAP_UNKNOWN,对端设备的io状态,指明对端设备的输入输出能力,参数包含BTM_IO_CAP_OUT仅可以显示(DisplayOnly),BTM_IO_CAP_IO显示yes|no(DisplayYesNo),BTM_IO_CAP_IN仅可以键盘输入(KeyBoardOnly),BTM_IO_CAP_NONE既没有输入也没有输出(NoInputNoOutput),BTM_IO_CAP_KBDISP键盘显示(Keyboard Display),BTM_IO_CAP_MAX指明io状态标志共有多少种,BTM_IO_CAP_UNKNOWN状态未知

说白了io_caps决定了你后续配对的方式举个例子对端是个耳机,既不能输入也不能输出,那就不能通过密码输入或比对密码的方式了,因为耳机即不能输入密码也不能显示出密码来进行确认设备对象初始化创建之后,接着在btm_sec_alloc_dev继续填充信息
 1  p_inq_info = BTM_InqDbRead(bd_addr);
2  if (p_inq_info != NULL) {
3    memcpy(p_dev_rec->dev_class, p_inq_info->results.dev_class, DEV_CLASS_LEN);
4
5    p_dev_rec->device_type = p_inq_info->results.device_type;
6    p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type;
7  } else if (bd_addr == btm_cb.connecting_bda)
8    memcpy(p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN);
9
10  /* update conn params, use default value for background connection params */
11  memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS));
12
13  p_dev_rec->bd_addr = bd_addr;
14
15  p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
16  p_dev_rec->hci_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_BR_EDR);
首先从蓝牙的搜索结果数据库中查询该设备信息,显而易见,如果是拿到扫描结果之后发起配对,那么就可以查到设备信息按照发起端的逻辑来看,根据扫描结果p_dev_rec需要更新的参数有
dev_class,device_type,ble.ble_addr_type同时更新conn_params连接参数
  • dev_class:设备class

  • device_type:对端设备类型,三种设备类型br_edr,ble和dumo双模

  • ble.ble_addr_type:蓝牙设备ble地址类型,这又是个大的调研方向,总的来说ble地址分固定和可变的,长期固定是要钱滴,具体后续吧

  • conn_params:le连接相关的一些参数,具体参数有哪些可参考tBTM_LE_CONN_PRAMS

  • bd_addr:设备蓝牙地址

  • ble_hci_handle

  • hci_handle

ok继续回到btm_sec_bond_by_transport,条件检查啥的就不说了接下来又对设备信息进行了第三次更新
  • security_required:指定为BTM_SEC_OUT_AUTHENTICATE发出的认证请求

  • is_originator:指定为true,表明是否为连接发起端

还有对sec_flags的修改
1p_dev_rec->sec_flags &=
2      ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED |
3        BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);
创建设备记录时已经指定过flags为BTM_SEC_IN_USE,当前连接security的一个状态此处赋值表明sec_flags不是以上其中的一种,毕竟是刚创建的设备,所以从安全角度来讲,目前linkKey未知。也为进行Authentication和Encrypt,同时linkKey也没进行授权,很好理解如果本机蓝牙controller不支持SSP,那就用旧版的pin码方式配对
但蓝牙2.1之后为SSP,所以接着往支持SSP的方向上看除了设备信息,还和btm相关信息有关,btm_cb的初始化在蓝牙开启过程中
1//system/bt/stack/btm/btm_main.cc
2void btm_init(void) {
3  btm_cb.Init(stack_config_get_interface()->get_pts_secure_only_mode()
4                  ? BTM_SEC_MODE_SC
5                  : BTM_SEC_MODE_SP);
6}
感兴趣的可以看下根据设备信息tBTM_SEC_DEV_REC和全局的btm控制块tBTM_CB信息,接下来会执行
1 btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
2      status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
捋一下
 1tBTM_STATUS btm_sec_bond_by_transport(const RawAddress& bd_addr,
2                                      tBT_TRANSPORT transport, uint8_t pin_len,
3                                      uint8_t* p_pin, uint32_t trusted_mask[]) {
4  ...
5  tACL_CONN* p = btm_bda_to_acl(bd_addr, transport);
6 ...
7  /* Other security process is in progress */
8  if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {
9    ...
10   //正在忙于配对
11    return (BTM_WRONG_MODE);
12  }
13
14  //创建设备信息初始化基础参数并记录在数据库
15  p_dev_rec = btm_find_or_alloc_dev(bd_addr);
16  if (p_dev_rec == NULL) {
17   //设备记录信息创建失败,说明资源不足,终止配对
18    return (BTM_NO_RESOURCES);
19  }
20
21  if (!controller_get_interface()->get_is_ready()) {
22    //controller未准备好,终止配对
23    return (BTM_NO_RESOURCES);
24  }
25  ...
26
27  /* 若已配对或有存货的acl连接,则终止配对*/
28  ...
29  if ((BTM_DeleteStoredLinkKey(&bd_addr, NULL)) != BTM_SUCCESS)
30   //删除所存储的该设备的linkKey
31    return (BTM_NO_RESOURCES);
32
33   ...
34  if ((...) {
35      //修改配对状态
36      btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
37     //读取远端设备name
38      status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
39    }
40  } 
41...
42
43  if (status != BTM_CMD_STARTED) {
44    BTM_TRACE_ERROR(
45        "%s BTM_ReadRemoteDeviceName or btm_sec_dd_create_conn error: 0x%x",
46        __func__, (int)status);
47    //读取对端设备name或者建链失败
48    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
49  }
50
51  return status;
52}
代码很长,摘取一部分,主要的就是修改配对状态和读取远端设备name修改配对状态继续调用,此处修改的时btm_cb控制块的pairing状态: IDLE--->GET_REM_NAME
 1//system/bt/stack/btm/btm_sec.cc
2static void btm_sec_change_pairing_state(tBTM_PAIRING_STATE new_state) {
3//修改btm_cb的配对状态为当前传入的状态
4 btm_cb.pairing_state = new_state;
5...
6 //更新lcb配对状态
7 l2cu_update_lcb_4_bonding(btm_cb.pairing_bda, true);
8//启动延时任务,若一定时间内为配对完成,则会调用配对超时
9    alarm_set_on_mloop(btm_cb.pairing_timer, BTM_SEC_TIMEOUT_VALUE * 1000,
10                       btm_sec_pairing_timeout, NULL);
11}
看起来该状态时就两件事儿,一个是更新lcb即link控制块状态,另一个就是启动一个配对超时检测接下来读取远程设备name
 1tBTM_STATUS BTM_ReadRemoteDeviceName(const RawAddress& remote_bda,
2                                     tBTM_CMPL_CB* p_cb,
3                                     tBT_TRANSPORT transport) {
4  ...
5
6  ...
7  /* BR/EDR和双模设备,读取远程设备name */
8  return btm_initiate_rem_name(remote_bda, BTM_RMT_NAME_EXT,
9                               BTM_EXT_RMT_NAME_TIMEOUT_MS, p_cb);
10}
协议栈开始向controller发送hci命令,读取远程设备name信息
1//启动任务超时监控
2 alarm_set_on_mloop(p_inq->remote_name_timer, timeout_ms,
3                         btm_inq_remote_name_timer_timeout, NULL);
4//向controller发送hci命令
5  btsnd_hcic_rmt_name_req(
6            remote_bda, p_cur->results.page_scan_rep_mode,
7            p_cur->results.page_scan_mode,
8            (uint16_t)(p_cur->results.clock_offset | BTM_CLOCK_OFFSET_VALID));
最终通过hci层向controller发送命令
1//system/bt/stack/btu/btu_hcif.cc
2void btu_hcif_send_cmd(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_buf) {
3     ···
4hci_layer_get_interface()->transmit_command(
5      p_buf, btu_hcif_command_complete_evt, btu_hcif_command_status_evt,
6      vsc_callback);
7      ...
8}
hci命令的操作码也已经在btsnd_hcic_rmt_name_req中指明
操作码为HCI_RMT_NAME_REQUEST向controller发送命令时携带了两个event回调,一个是事件处理完成的回调btu_hcif_command_complete_evt,一个是命令状态改变的回调btu_hcif_command_status_evt代码流程貌似到此戛然而止了,从settings-->framework-->bluetooth-->jni-->stack除了有一个bonding状态的回调,其他就是从应用到协议栈的单向调用,最终以协议栈向controller发送HCI_RMT_NAME_REQUEST命令结束这就结束了?作为发起端来说,在选择设备进行配对时会弹出配对弹窗,携带password信息这个弹窗怎么触发弹起的?password又是怎么得来的?还好在上一篇文章中我们看到了hci日志,在协议栈请求Name结束后,controller会发送事件HCI_Remote_Name_Request_Complete给host这里插一个分析,host如何监听controller的事件的呢?如下所示
 1//system/bt/main/bte_main.cc
2void post_to_main_message_loop(const base::Location& from_here, BT_HDR* p_msg) {
3  if (do_in_main_thread(from_here, base::Bind(&btu_hci_msg_process, p_msg)) !=
4      BT_STATUS_SUCCESS) {
5    LOG(ERROR) << __func__ << ": do_in_main_thread failed from "
6               << from_here.ToString();
7  }
8}
9
10void bte_main_init(void) {
11 ...
12  hci->set_data_cb(base::Bind(&post_to_main_message_loop));
13}
在蓝牙开启之后会给hci添加event数据回调也即是说当controller有事件上报给协议栈时会触发btu_hci_msg_process方法那就直接看该方法的处理喽
1void btu_hci_msg_process(BT_HDR* p_msg) {
2...
3  case BT_EVT_TO_BTU_HCI_EVT:
4      btu_hcif_process_event((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
5      osi_free(p_msg);
6      break;
7...
8}
在controller接收到name请求之后,会向host发送事件通知
  • HCI_Remote_Host_Supported_Features_Notification:远程主机所支持的LMP扩展功能

  • HCI_Remote_Name_Request_Complete:远程设备name信息获取完成

HCI_Remote_Host_Supported_Features_Notification用于告知对端设备所支持的功能接收到通知后会更新设备记录结构体的sm4字段
1void btm_sec_rmt_host_support_feat_evt(uint8_t* p) {
2...
3    if (HCI_SSP_HOST_SUPPORTED(features)) {
4      p_dev_rec->sm4 = BTM_SM4_TRUE;
5    }
6 ...
7}
当请求远程设备的name有结果后,会回调btu_hcif_process_event
1//system/bt/stack/btu/btu_hcif.cc
2void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_msg) {
3...
4  case HCI_RMT_NAME_REQUEST_COMP_EVT:
5      btu_hcif_rmt_name_request_comp_evt(p, hci_evt_len);
6      break;
7...
8}
又是一级一级的回传,回传到
1//system/bt/stack/btu/btu_hcif.cc
2static void btu_hcif_rmt_name_request_comp_evt(uint8_t* p, uint16_t evt_len) {
3  ...
4  btm_process_remote_name(&bd_addr, p, evt_len, status);
5  btm_sec_rmt_name_request_complete(&bd_addr, p, status);
6}
开始兵分两路,来分别看看干了什么吧越写越多,但目前却只写了流程图中的极少一部分跟你们一样,太长了我也没啥耐心看,剩下的改天在写总结一下,到目前为止
  • 协议栈对btm_cb控制块的pairing_state进行了更新:IDEL-->GET_REM_NAME

  • 创建了设备记录p_dev_rec结构体

  • 删除了controller内所存储的该设备的linkKey

  • 向controller发送获取name的请求,同时接收到了回复

其他的下一篇更新....https://mp.weixin.qq.com/s/U_gg9UnVwKgoE_kfbZNaUA