之前看到某位大神的博客android -- 蓝牙 bluetooth (三)搜索蓝牙,介绍了Android中蓝牙的搜索过程,从framework到service,再到JNI、bluetooth stack,大致的流程讲得很清楚。这里我了解了一下Android4.4中bluetooth stack中的代码流程,总结如下。
在JNI层中调用startDiscoveryNative函数,最终指向stack层bluetoothInterface(bluetooth.c)结构的成员函数start_discovery,这便是stack层的入口。在btif_dm_start_discovery(btif_dm.c)函数中设置了discovery的相关参数,并调用BTA_DmSearch(&inq_params, services, bte_search_devices_evt)来执行。这个函数的内容如下:
void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
{
tBTA_DM_API_SEARCH *p_msg;
if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
{
memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));
p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
p_msg->services = services;
p_msg->p_cback = p_cback;
p_msg->rs_res = BTA_DM_RS_NONE;
bta_sys_sendmsg(p_msg);
}
}
可以看到,该函数构造了一个消息p_msg,设置了事件类型以及回调函数。在这里,回调函数即前面的bte_search_devices_evt,它处理搜索的结果,并最终通过JNI提供的callback返回给上层。这个消息被发送到btu_task,由bta模块处理,代码如下:
BTA_API void bta_sys_event(BT_HDR *p_msg)
{
UINT8 id;
BOOLEAN freebuf = TRUE;
APPL_TRACE_EVENT1("BTA got event 0x%x", p_msg->event);
/* get subsystem id from event */
id = (UINT8) (p_msg->event >> 8);
/* verify id and call subsystem event handler */
if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL))
{
freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
}
else
{
APPL_TRACE_WARNING1("BTA got unregistered event id %d", id);
}
if (freebuf)
{
GKI_freebuf(p_msg);
}
}
其内容很简单,就是暗中event的id来调相应的函数来处理。对于search过程来说,他的处理函数早在蓝牙初始化中调用函数
BTA_EnableBluetooth中注册:
bta_sys_register (BTA_ID_DM_SEARCH, &bta_dm_search_reg );
从而,代码转入bta_dm_search_reg的成员函数bta_dm_search_sm_execute。它的实现跟上面的bta_sys_event很类似,根据不同的state来调用相应状态下的函数。代码如下:
BOOLEAN bta_dm_search_sm_execute(BT_HDR *p_msg)
{
tBTA_DM_ST_TBL state_table;
UINT8 action;
int i;
APPL_TRACE_EVENT2("bta_dm_search_sm_execute state:%d, event:0x%x",
bta_dm_search_cb.state, p_msg->event);
/* look up the state table for the current state */
state_table = bta_dm_search_st_tbl[bta_dm_search_cb.state];
bta_dm_search_cb.state = state_table[p_msg->event & 0x00ff][BTA_DM_SEARCH_NEXT_STATE];
/* execute action functions */
for (i = 0; i < BTA_DM_SEARCH_ACTIONS; i++)
{
if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_DM_SEARCH_IGNORE)
{
(*bta_dm_search_action[action])( (tBTA_DM_MSG*) p_msg);
}
else
{
break;
}
}
return TRUE;
}
刚进入这个函数时,由于bta_dm_search_cb.state为0,故state_table为
bta_dm_search_idle_st_table;由于前面在BTA_DmSearch中设置的event为BTA_DM_API_SEARCH_EVT(0x0200),从而第一个真正的执行函数为bta_dm_search_start。它的关键部分如下:
void bta_dm_search_start (tBTA_DM_MSG *p_data)
{
......
/* save search params */
bta_dm_search_cb.p_search_cback = p_data->search.p_cback;
bta_dm_search_cb.services = p_data->search.services;
......
result.status = BTM_StartInquiry( (tBTM_INQ_PARMS*)&p_data->search.inq_params,
bta_dm_inq_results_cb,
(tBTM_CMPL_CB*) bta_dm_inq_cmpl_cb);
......
}
在第一部分,它注册了search的回调函数给全局变量bta_dm_search_cb的成员;同时,它在第二部分调用BTM_StartInquiry去执行inquiry(扯了这么多其实还没到真正发HCI command的地方)。同样的,将函数BTM_StartInquiry的关键部分贴在下面:
*******************************************************************************/
tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb,
tBTM_CMPL_CB *p_cmpl_cb)
{
tBTM_STATUS status = BTM_CMD_STARTED;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
......
/* Initialize the inquiry variables */
p_inq->state = BTM_INQ_ACTIVE_STATE;
p_inq->p_inq_cmpl_cb = p_cmpl_cb;
p_inq->p_inq_results_cb = p_results_cb;
p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */
p_inq->inq_active = p_inqparms->mode;
......
}
很简单,它再次设置了bta_dm_search_cb的一些成员,最重要的即扫描结果的回调函数p_results_cb(即bta_dm_inq_results_cb)和扫描完成(超时或者收到results的数目超过设定)的处理函数
p_cmpl_cb(即bta_dm_inq_cmpl_cb)。这样,在收到inquiry的results后便有相应的处理函数。在设置完这些参数后,函数后半段便要开始执行扫描了,最终通过btu_hcif_send_cmd将BR\EDR或者LE扫描的HCI指令发送给controller。同时,它还会对上层进行通知,告知执行已经执行,现在的状态是discovery_started。而当收到扫描的report时,调用callback函数上报device_found。只有当用于通过UI选择与某一个设备进行配对时,才会进入后面的配对鉴权、加密等过程,这些留待以后慢慢学习。