为什么不回连——被removebond的蓝牙HOGP Device
为什么device那边产生了disconnect,master这边就允许其回连;而master这边将device removebond掉,device就不能回连了呢?
今天我们来看看,master在removebondt的过程中到底做了些什么。
前面一篇博客说道,bluedroid中的蓝牙HOGP设备在因省电而disconnect后,master可以允许其快速回连,省去中间发杂的smp(安全管理)、gatt搜索和hid搜索的过程。当时提到过,master这边的host将对端HOGP device加入到了四个全局结构数组中,从而这个device才可以快速回连;而当master主动解除device的配对后,device就不可以回连了。这样看来,master端的host在removebond的过程中必然对这几个全局变量做了些什么。因此,我们就来看看host对这几个变量做了什么。
先是settings中解除配对时,bluedroid对上层的接口——remove_bond,它执行的流程大概是这样的:
==>btif_dm_remove_bond
==>btif_dm_cb_remove_bond
==>bta_dm_remove_device——这一步展开
/*******************************************************************************
**
** Function bta_dm_remove_device
**
** Description Removes device, Disconnects ACL link if required.
****
*******************************************************************************/
void bta_dm_remove_device (tBTA_DM_MSG *p_data)
{
tBTA_DM_API_REMOVE_DEVICE *p_dev = &p_data->remove_dev;
int i;
tBTA_DM_SEC sec_event;
#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)
/* need to remove all pending background connection before unpair */
BTA_GATTC_CancelOpen(0, p_dev->bd_addr, FALSE); //step 1,删除部分关于whitelist的记录
#endif
if (BTM_IsAclConnectionUp(p_dev->bd_addr))
{
/* Take the link down first, and mark the device for removal when disconnected */
btm_remove_acl( p_dev->bd_addr) ; //step 2,发送hci cmd——disconnect
for(i=0; i<bta_dm_cb.device_list.count; i++)
{
if(!bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_dev->bd_addr))
break;
}
if(i < bta_dm_cb.device_list.count)
{
bta_dm_cb.device_list.peer_device[i].conn_state = BTA_DM_UNPAIRING; //step 3,删除所有记录(gatt cache、hid cache等)的标志
}
}
else /* Ok to remove the device in application layer */
{
BTM_SecDeleteDevice(p_dev->bd_addr);
#if (BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE)
/* remove all cached GATT information */
BTA_GATTC_Refresh(p_dev->bd_addr);
#endif
if( bta_dm_cb.p_sec_cback )
{
bdcpy(sec_event.link_down.bd_addr, p_dev->bd_addr);
/* No connection, set status to success (acl disc code not valid) */
sec_event.link_down.status = HCI_SUCCESS;
bta_dm_cb.p_sec_cback(BTA_DM_DEV_UNPAIRED_EVT, &sec_event);
}
}
}
这里分为三步,第一步是BTA_GATTC_CancelOpen(0, p_dev->bd_addr, FALSE),它打算将设备从“whitelist”中删除。之所以称为“打算”,一是因为它根本没成功,而是删除也是host端关于whitelist的记录,也就是前面提到的四个全局数组(其实是一个全局结构的四个成员数组)。下面把每个全局数组的delete流程过一遍(按照删除的顺序),其中有一个重点看下,因为它可能导致从“whitelist”中删除失败。
==>bta_gattc_process_api_open_cancel
==>bta_gattc_cancel_bk_conn
==>(1) bta_gattc_mark_bg_conn——bta_gattc_cb.bg_track中记录删除
==>(2) GATT_CancelConnect
==>gatt_remove_bg_dev_for_app
==>gatt_update_auto_connect_dev
==>gatt_remove_bg_dev_from_list
==>(1) btm_update_bg_conn_list——btm_cb.ble_ctr_cb.bg_dev_list中的记录被删除
==>(2) btm_update_dev_to_white_list——STOP HERE!Let's see what happened.
/*******************************************************************************
**
** Function btm_update_dev_to_white_list
**
** Description This function adds a device into white list.
*******************************************************************************/
BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, UINT8 attr)
{
/* look up the sec device record, and find the address */
tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
BOOLEAN started = FALSE;
UINT8 wl_state = p_cb->wl_state;
BTM_TRACE_DEBUG3("to_add(%d), attr(%d), wl_state(%d)", to_add, attr, wl_state);
if ((to_add && p_cb->num_empty_filter == 0) ||
(!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries))
{
BTM_TRACE_ERROR1("WL full or empty, unable to update to WL. num_entry available: %d",
p_cb->num_empty_filter);
return started;
}
btm_suspend_wl_activity(wl_state);
/* enq pending WL device operation */
btm_enq_wl_dev_operation(to_add, bd_addr, attr);
btm_resume_wl_activity(wl_state);
return started;
}
本来呢,这一步是要将btm_cb.ble_ctr_cb.wl_op_q中的记录清理一下的,具体是在btm_enq_wl_dev_operation这里。但是呢,你会发现它怎么也走不到那儿去!why?
它被前面的判断卡住,直接返回了!让我们来看看这个判断中关于删除设备记录的情况:
!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries
这to_add还好说,为0表示删除设备记录;这后面这么长的两位。。。什么情况?这要回到蓝牙初始化时,host向controller发的一条hci cmd——HCI_LE_Read_White_List_Size的处理函数:
/*******************************************************************************
**
** Function btm_read_white_list_size_complete
**
** Description This function read the current white list size.
*******************************************************************************/
void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len)
{
UINT8 status;
BTM_TRACE_DEBUG0("btm_read_white_list_size_complete ");
STREAM_TO_UINT8 (status, p);
if (status == HCI_SUCCESS)
{
STREAM_TO_UINT8(btm_cb.ble_ctr_cb.max_filter_entries, p);
btm_cb.ble_ctr_cb.num_empty_filter = btm_cb.ble_ctr_cb.max_filter_entries;//相同的值!
}
btm_get_ble_buffer_size();
}
看到了么,读取了controller的whitelist size之后,host将两个变量设置为相同的值!
那么,它们会不会变化呢?后者是个“max”,肯定是不会变的;而前者在以下情况会发生改变:
1)设备被成功添加到whitelist中——btm_cb.ble_ctr_cb.num_empty_filter--;
2)设备被成功地从whitelist中删除——btm_cb.ble_ctr_cb.num_empty_filter++;
那么问题来了,如果device还没有被添加到whitelist中(更不用说删除了),两者的值是一样的,这样就刚好符合前面在btm_update_dev_to_white_list中的判断条件——打印一句log就不干了!这样做是不合理的,但是可能没出大问题,所以就一直都留着了。另一方面,即使曾经被添加到whitelist中过(也就是回连过),通过这里的判断也不会有什么不同。在添加到whitelist中的时候,host早已将这个device从btm_cb.ble_ctr_cb.wl_op_q中删除。这样看来,whitelist是只有增没有减啊!这一来不晓得后面回连的HOGP device会不会把controller的whitelist填满(填满了host自己也不知道,它统计的数量不正确!);二来是被disconnect掉的设备还有回连的机会(host也没那么挫,只是device有回连的可能,可能而已!),一旦连上就会不停地发空packet,但是不会连profile(why?),不过省电这事儿就别想了。
说道这里,removebond准备工作的第一步就算是完成了。不过似乎还有一个数组——gatt_cb.bgconn_dev中的记录还没有删除。没错,在发送disconnect前,这个全局数组中的记录并没有被删除。。看名字就知道是gatt的接口,因此它当中device的删除也会等到gatt被层的连接被断掉。具体是在收到disconnect complete event后,gatt的connection处理函数执行过程中,调用bta_gattc_close来实现的。第二步很直接,它会直接调用btsnd_hcic_disconnect (hci_handle, HCI_ERR_PEER_USER),告诉对方disconnect的reason是HCI_ERR_PEER_USER,所以你就甭想回连了,我们这边不接受。第三步是一句设置,不过这简单的一句却决定了后面清除gatt_cache、hid cache和link key等一系列操作!因此,removeBond基本上就宣告该所有关于device的记录均将被清除,不留痕迹。