好久没写东西了,今天回来看看,发现以前写的还不算太死板。写写东西也正好理理思路。
废话不多说,进入今天的主题——
Android4.4 环境下 BLE HOGP设备的自动回连
何为BLE?子曰bluetooth low energy,也就是低功耗蓝牙。BLE它是蓝牙SIG后来提出来的,最早的蓝牙是BR\EDR.。
何为HOGP?子曰hid over gatt,也就是基于GATT profile的hid。
那又为何要回连?一般的hid设备为了方便使用,都不会去使用限制用户操作的电源线来供电,而是使用电池。因此省电是对hid设备的一个基本需求,谁愿意带着每过几小时就得充电的蓝牙手环?因此当设备长时间不在使用时,蓝牙的连线应该为了省电而断开;而为了随时都可以再次使用蓝牙设备,回连就显得有必要,从而避免每次花费数十秒的重新建立连线的时间。其实原本蓝牙的hid设备都支持回连,只是那种情况是由hid device来主动连接。而BLE既然自称低功耗,回连就更不可少了。
下面就从Android4.4的bluedroid中看看,BLE HOGP设备的回连实现。
协议签订好,您就是我们的贵宾了!
首先,当底层acl链路建立起来后,作为HOGP slave的hid device和对端的master就要开始签订一些协议了(虽然称作profile,但个人觉得就是些协议)。GATT需要协商,看看HOGP device都有哪些属性(ATT,即Attribute);而HOGP则要看看你HOGP device的GATT属性值如何,从而决定master该如何通过GATT来处理HOGP device发送过来的消息。当所有的协议都签订好后,这个角色名为slave的HOGP device就将成为master的贵宾了。
为什么这么说呢?看看下面的代码:
-
/*******************************************************************************
-
**
-
** Function bta_hh_le_open_cmpl
-
**
-
** Description HID over GATT connection sucessfully opened
-
**
-
*******************************************************************************/
-
void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb)
-
{
-
APPL_TRACE_DEBUG1("%s", __FUNCTION__);
-
if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE)
-
{
-
#if BTA_HH_DEBUG
-
bta_hh_le_hid_report_dbg(p_cb);
-
#endif
-
bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE);
-
bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL);
-
#if (BTA_HH_LE_RECONN == TRUE)
-
if (p_cb->status == BTA_HH_OK)
-
{
-
bta_hh_le_add_dev_bg_conn(p_cb, TRUE);
-
}
-
#endif
-
}
-
}
这里是协议签订后的处理。你看注释上不是写着“HID over GATT connection sucessfully opened”么?
之所有称这个slave即将成为我们的贵宾,因为上面的代码它调用bta_hh_le_add_dev_bg_conn,将这个HOGP device加到了表示whitelist的列表中。从此这个设备可以随时断开蓝牙链路,又随时可以再连回来,只要随便按个按键即可。是不是很任性?就像是高级会员才有的权利!看看它究竟做了点什么
-
1.BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE);
-
2.BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL);
如果你看过经由GATT接口(而不是settings里头点击设备配对)来建立连接的代码,你就会发现,它和background connection建立的过程类似,只是顺序反了过来。这里的第1步做了很多事儿,它将这个HOGP device先后加到了四个全局的结构数组中:
a)bta_gattc_cb.bg_track
b)gatt_cb.bgconn_dev
c)btm_cb.ble_ctr_cb.bg_dev_list
d)btm_cb.ble_ctr_cb.wl_op_q
有什么用呢?这里仅仅是添加了,并没有真正去执行添加到whitelist中的动作。不过,这里的d)就有点类似host端whitelist的意思,后面会看到,只有在这个里头的HOGP device才会被添加到control(如果不知道host和control,请自觉去脑补蓝牙spec)的whitelist中,controller才会主动连接该设备。而c)项是另一个关于host端的whitelist记录,它和d)配合使用,HOGP device才会真正的被回连。至于剩下的两个,本文不会涉及到,不过这里简单提下。a)如其名是用来track的,我发现在出现某些bug时这个变量才会找到自己的存在感(至于是神马bug,你可以试试多个HOGP device连接同一个master的情况);而b)是gatt层对“贵宾”的记录,对于同一个HOGP device可以有不同gatt_if,但是whitelist只认蓝牙地址,因此只需要在第一次见到这个device时将它设置到host端的whitelist中,有点像”既然您是贵宾,您的家人也是贵宾,我们就不再专门做记录“的意思,不过我还没见过实际的情况。
至于第2步,其实它啥也没干,就只是将btm_cb.ble_ctr_cb.bg_conn_type设置成了BTA_DM_BLE_CONN_AUTO,它原来应该是BTM_BLE_CONN_NONE。其实在第1步中,它还会走到下面这段代码中:
-
BOOLEAN btm_ble_resume_bg_conn(void)
-
{
-
tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
-
BOOLEAN ret = FALSE;
-
if (p_cb->bg_conn_type != BTM_BLE_CONN_NONE)
-
{
-
if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO)
-
ret = btm_ble_start_auto_conn(TRUE);
-
if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE)
-
ret = btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback);
-
}
-
return ret;
-
}
看吧,第1步它差点就走到了btm_ble_start_auto_conn,直接去执行le create connection了!不过还好,那时候的btm_cb.ble_ctr_cb.bg_conn_type为BTM_BLE_CONN_NONE。这里提下个人的发现,btm_cb.ble_ctr_cb.bg_conn_type一旦被设置为BTA_DM_BLE_CONN_AUTO了,就再也不会变回BTM_BLE_CONN_NONE了。或许有人会问了,后来的HOGP device走到上面的代码会怎么样呢,会在连接已经建立的时候去傻乎乎的再次le create connection么?答案是——你想多了。
我们提供优质的回连服务,您无需多虑!
走完上面两步,一切就绪,就等你HOGP device发起disconnect了!不管你多任性,我们都提供优质的回连服务!看看我们如何应对disconnect:
==>btu_hcif_disconnection_comp_evt
==>btm_sec_disconnected
==>btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, p_dev_rec->bd_addr, FALSE)
==>btm_ble_resume_bg_conn
这里再次回到这个函数,看来它和回连有很大的渊源,去看看被它调用的btm_ble_start_auto_conn:
-
/*******************************************************************************
-
**
-
** Function btm_ble_start_auto_conn
-
**
-
** Description This function is to start/stop auto connection procedure.
-
**
-
** Parameters start: TRUE to start; FALSE to stop.
-
**
-
** Returns void
-
**
-
*******************************************************************************/
-
BOOLEAN btm_ble_start_auto_conn(BOOLEAN start)
-
{
-
tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
-
BD_ADDR dummy_bda = {0};
-
BOOLEAN exec = TRUE;
-
UINT8 own_addr_type = BLE_ADDR_PUBLIC;
-
UINT16 scan_int, scan_win;
-
if (start)
-
{
-
if (p_cb->conn_state == BLE_CONN_IDLE && btm_ble_count_unconn_dev_in_whitelist() > 0)<span style="white-space:pre"> </span>//step 1
-
{
-
btm_execute_wl_dev_operation();<span style="white-space:pre"> </span>//step 2
-
scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int;
-
scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_SCAN_SLOW_WIN_1 : p_cb->scan_win;
-
if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */
-
scan_win, /* UINT16 scan_win */
-
0x01, /* UINT8 white_list */
-
BLE_ADDR_PUBLIC, /* UINT8 addr_type_peer */
-
dummy_bda, /* BD_ADDR bda_peer */
-
own_addr_type, /* UINT8 addr_type_own, not allow random address for central */
-
BTM_BLE_CONN_INT_MIN_DEF, /* UINT16 conn_int_min */
-
BTM_BLE_CONN_INT_MAX_DEF, /* UINT16 conn_int_max */
-
BTM_BLE_CONN_SLAVE_LATENCY_DEF, /* UINT16 conn_latency */
-
BTM_BLE_CONN_TIMEOUT_DEF, /* UINT16 conn_timeout */
-
0, /* UINT16 min_len */
-
0)) /* UINT16 max_len */
-
{
-
/* start auto connection failed */
-
exec = FALSE;
-
}
-
else
-
{
-
btm_ble_set_conn_st (BLE_BG_CONN);
-
}
-
}
-
else
-
{
-
exec = FALSE;
-
}
-
}
-
else
-
{
-
if (p_cb->conn_state == BLE_BG_CONN)
-
{
-
btsnd_hcic_ble_create_conn_cancel();
-
btm_ble_set_conn_st (BLE_CONN_CANCEL);
-
}
-
}
-
return exec;
-
}
这里我们不去关注回连使用的le create connection使用了那些参数,只看回连中调用的函数。
step 1:两个判断
第一个很明显,我们之前设置过了。那第二个呢?插入代码:
-
UINT8 btm_ble_count_unconn_dev_in_whitelist(void)
-
{
-
tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
-
UINT8 i, count = 0;
-
for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM; i ++)
-
{
-
if (p_cb->bg_dev_list[i].in_use &&
-
!BTM_IsAclConnectionUp(p_cb->bg_dev_list[i].bd_addr))
-
{
-
count ++;
-
}
-
}
-
return count;
-
}
你瞧,前面添加的btm_cb.ble_ctr_cb.bg_dev_list派上用场了吧。这里数了数在btm_cb.ble_ctr_cb.bg_dev_list这个数组中并且没有acl链路(你看,它也不会傻到有acl链路还去再建)的设备有多少个,将总数返回。既然前面添加过,这里就一定返回大于0的结果。因此,两条判断pass,进入step 2。
step 2:
添加到贵宾列表——controller的whitelist中去:
-
/*******************************************************************************
-
**
-
** Function btm_execute_wl_dev_operation
-
**
-
** Description execute the pending whitelist device operation(loading or removing)
-
*******************************************************************************/
-
BOOLEAN btm_execute_wl_dev_operation(void)
-
{
-
tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q;
-
UINT8 i = 0;
-
BOOLEAN rt = TRUE;
-
for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && rt; i ++, p_dev_op ++)
-
{
-
if (p_dev_op->in_use)
-
{
-
rt = btm_add_dev_to_controller(p_dev_op->to_add, p_dev_op->bd_addr, p_dev_op->attr);
-
memset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP));
-
}
-
else
-
break;
-
}
-
return rt;
-
}
看看,这里是调用btm_add_dev_to_controller来将我们的HOGP device加到controller的whitelist中了。此外,被添加的设备也被从btm_cb.ble_ctr_cb.wl_op_q中清除了。这里头还有疑问么?(你以为我会告诉你,这里把所有的device,不管连着还是断开的,都加到whitelist中了么?)
其实还有一个step 3的,就是上面调用btsnd_hcic_ble_create_ll_conn告诉controller使用whitelist来建立BLE链路。不过它太明显了,就不多说了。
到了这里,我们的服务员就已经准备好了,就等着HOGP device上门回连了。没错,是上门回连,因为master只有通过slave的advertisement才能找到HOGP device并重新建立BLE链路。只是这次,master有link key,也有gatt的cache和HOGP的cahce(也可能没有,你可以猜猜看是什么时候),因此省去了很大一块儿SMP和GATT搜索的时间。
服务评估:
说来说去,服务好不好都得看bluedroid代码设计的如何。如果顾及的情况不全面,那就会有问题。不过我们不该去吐槽bluedroid的提供者,它”偶然“留下的bug也让我等码农们的生活充实了不少,每次跟boss汇报都有了可观的工作量。下面列几条自动回连服务中存在的疑问:
1.一个HOGP设备还好,多个设备又会如何?
2.如果回连的时候master没有gatt cache,那么回连还快得起来么?
3.HOGP device断开链路master就该设置回连,那master想主动断开device,情况又是如何呢?
[cpp] view plain copy