setting 界面开始搜索的时候,通常也会同时进行le scan,这一点在inquiry流程之命令下发中已经讲述。此篇文章主要是分析一下对于controller 搜索到的广播包的处理。这里以Android O的bluedroid的代码作为分析对象。

void btu_hci_msg_process(BT_HDR* p_msg) {
/* Determine the input message type. */
switch (p_msg->event & BT_EVT_MASK) {
case BT_EVT_TO_BTU_HCI_ACL:
/* All Acl Data goes to L2CAP */
l2c_rcv_acl_data(p_msg);
break; case BT_EVT_TO_BTU_L2C_SEG_XMIT:
/* L2CAP segment transmit complete */
l2c_link_segments_xmitted(p_msg);
break; case BT_EVT_TO_BTU_HCI_SCO:
#if (BTM_SCO_INCLUDED == TRUE)
btm_route_sco_data(p_msg);
break;
#endif case BT_EVT_TO_BTU_HCI_EVT:
btu_hcif_process_event((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
osi_free(p_msg);
break; case BT_EVT_TO_BTU_HCI_CMD:
btu_hcif_send_cmd((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
break; default:
osi_free(p_msg);
break;
}
}

上面可以看出 btu_hci_msg_process 的所有的处理对象。hci event 的处理函数 是btu_hcif_process_event((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);

void btu_hcif_process_event(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_msg) {
uint8_t* p = (uint8_t*)(p_msg + ) + p_msg->offset;
uint8_t hci_evt_code, hci_evt_len;
uint8_t ble_sub_code;
STREAM_TO_UINT8(hci_evt_code, p);
STREAM_TO_UINT8(hci_evt_len, p); switch (hci_evt_code) {
case HCI_INQUIRY_COMP_EVT:
btu_hcif_inquiry_comp_evt(p);
break;
case HCI_INQUIRY_RESULT_EVT:
btu_hcif_inquiry_result_evt(p);
break;
case HCI_INQUIRY_RSSI_RESULT_EVT:
btu_hcif_inquiry_rssi_result_evt(p);
break;
...
case HCI_BLE_EVENT: { //le 相关的event
STREAM_TO_UINT8(ble_sub_code, p); uint8_t ble_evt_len = hci_evt_len - ;
switch (ble_sub_code) { //判断子event
case HCI_BLE_ADV_PKT_RPT_EVT: /* result of inquiry */
HCI_TRACE_EVENT("HCI_BLE_ADV_PKT_RPT_EVT");
btm_ble_process_adv_pkt(ble_evt_len, p);//处理广播包
break;
case HCI_BLE_CONN_COMPLETE_EVT:
btu_ble_ll_conn_complete_evt(p, hci_evt_len);
break;
case HCI_BLE_LL_CONN_PARAM_UPD_EVT:
btu_ble_ll_conn_param_upd_evt(p, hci_evt_len);
break;
...

从上面可以看出 btu_hcif_process_event可以处理的event 的类型,并且可以看出处理广播包的函数 是 btm_ble_process_adv_pkt(ble_evt_len, p);从函数名称可以看出此时函数已经进入到btm 模块,其实现在btm_ble_gap.cc

/**
* This function is called when advertising report event is received. It updates
* the inquiry database. If the inquiry database is full, the oldest entry is
* discarded.
*/
void btm_ble_process_adv_pkt(uint8_t data_len, uint8_t* data) {
BD_ADDR bda;
uint8_t* p = data;
uint8_t legacy_evt_type, addr_type, num_reports, pkt_data_len;
int8_t rssi; /* Extract the number of reports in this event. */
STREAM_TO_UINT8(num_reports, p);//一个包里面可能有多个event,但是通常只有一个event while (num_reports--) { /* Extract inquiry results */
STREAM_TO_UINT8(legacy_evt_type, p);//event_type
STREAM_TO_UINT8(addr_type, p);//地址类型
STREAM_TO_BDADDR(bda, p);//地址
STREAM_TO_UINT8(pkt_data_len, p);//数据长度 uint8_t* pkt_data = p;
p += pkt_data_len; /* Advance to the the rssi byte */ STREAM_TO_INT8(rssi, p);//此时指针指向数据末尾的rssi btm_ble_process_adv_addr(bda, addr_type);//处理地址相关 uint16_t event_type;
if (legacy_evt_type == 0x00) { // ADV_IND;
event_type = 0x0013;
} else if (legacy_evt_type == 0x01) { // ADV_DIRECT_IND;
event_type = 0x0015;
} else if (legacy_evt_type == 0x02) { // ADV_SCAN_IND;
event_type = 0x0012;
} else if (legacy_evt_type == 0x03) { // ADV_NONCONN_IND;
event_type = 0x0010;
} else if (legacy_evt_type == 0x04) { // SCAN_RSP;
// We can't distinguish between "SCAN_RSP to an ADV_IND", and "SCAN_RSP to
// an ADV_SCAN_IND", so always return "SCAN_RSP to an ADV_IND"
event_type = 0x001B;
}
btm_ble_process_adv_pkt_cont(
event_type, addr_type, bda, PHY_LE_1M, PHY_LE_NO_PACKET, NO_ADI_PRESENT,
TX_POWER_NOT_PRESENT, rssi, 0x00 /* no periodic adv */, pkt_data_len,
pkt_data);//开始处理数据包
}
}

可以看出主要的 主要的处理流程是执行btm_ble_process_adv_pkt_cont,我们看看具体的实现:

/*******************************************************************************
**
** Function btm_ble_process_adv_pkt_cont
**
** Description This function is called after random address resolution is
** done, and proceed to process adv packet.
**
** Parameters
**
** Returns void
**
*******************************************************************************/
static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
{
tINQ_DB_ENT *p_i;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb;
tBTM_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var;
BOOLEAN update = TRUE;
UINT8 result = ; p_i = btm_inq_db_find (bda);
/* Check if this address has already been processed for this inquiry */
if (btm_inq_find_bdaddr(bda))//check 这个地址的设备是否已经被处理过
{
/* never been report as an LE device */
if (p_i &&
(!(p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) ||
/* scan repsonse to be updated */
(!p_i->scan_rsp)))//这里update的判断条件是device type是否已经定位以及是否有scan response
{
update = TRUE;
}
else if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))
{
update = FALSE;
}
else
{
/* if yes, skip it */
return; /* assumption: one result per event */
}
}
/* If existing entry, use that, else get a new one (possibly reusing the oldest) */
if (p_i == NULL)//没有entry,重新分配
{
if ((p_i = btm_inq_db_new (bda)) != NULL)
{
p_inq->inq_cmpl_info.num_resp++;
}
else
return;
}
else if (p_i->inq_count != p_inq->inq_counter) /* first time seen in this inquiry */ /*在这一次的inquiry 中第一次见到该设备*/
{
p_inq->inq_cmpl_info.num_resp++;
}
/* update the LE device information in inquiry database */
if (!btm_ble_update_inq_result(p_i, addr_type, evt_type, p))//更新数据库
return; if ((result = btm_ble_is_discoverable(bda, evt_type, p)) == )//检测adv包中标志位
{
LOG_WARN("%s device is no longer discoverable so discarding advertising packet pkt",
__func__);
return;
}
if (!update)
result &= ~BTM_BLE_INQ_RESULT;
...
/* background connection in selective connection mode */
BTM_TRACE_WARNING("btm_cb.ble_ctr_cb.bg_conn_type = %d libs_liu",btm_cb.ble_ctr_cb.bg_conn_type);
if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE)
{
if (result & BTM_BLE_SEL_CONN_RESULT)
btm_send_sel_conn_callback(bda, evt_type, p, addr_type);
else
{
BTM_TRACE_DEBUG("None LE device, can not initiate selective connection");
}
}
else
{
if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT))
{
(p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache);//处理结果
}
if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT))
{
(p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache);
}
}
}

从上面处理流程来看,主要是做了四件事:

  1. 查询设备数据库,并且判断该广播信息已经被处理过。
  2. btm_ble_update_inq_result(p_i, addr_type, evt_type, p) 更新设备数据库中的设备信息
  3. btm_ble_is_discoverable(bda, evt_type, p)  判断设备信息标志位,判断是否是可以发现的类型。
  4. 调用p_inq_results_cb 来处理 设备信息。这个在BTM_StartInquiry的时候传入参数bta_dm_inq_results_cb,调用的也就是这个回调函数。

下面主要分析一下2,3,4三个点:

2.btm_ble_update_inq_result(p_i, addr_type, evt_type, p)

/*******************************************************************************
**
** Function btm_ble_update_inq_result
**
** Description Update adv packet information into inquiry result.
**
** Parameters
**
** Returns void
**
*******************************************************************************/
BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p)
{
BOOLEAN to_report = TRUE;
tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results;
UINT8 len;
UINT8 *p_flag;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
UINT8 data_len, rssi;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var;
UINT8 *p1;
UINT8 *p_uuid16; STREAM_TO_UINT8 (data_len, p); btm_ble_cache_adv_data(p_cur, data_len, p, evt_type);// cache adv data p1 = (p + data_len);
STREAM_TO_UINT8 (rssi, p1); /* Save the info */
p_cur->inq_result_type = BTM_INQ_RESULT_BLE;
p_cur->ble_addr_type = addr_type;
p_cur->rssi = rssi;
/* active scan, always wait until get scan_rsp to report the result */
if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI && //如果是active scan,那么要等scan response 上来之后才会一起report,一个单独的BTM_BLE_CONNECT_EVT或者BTM_BLE_DISCOVER_EVT是不会上报的
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT)))
{
BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\
scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type);
p_i->scan_rsp = FALSE;
to_report = FALSE;
}
else
p_i->scan_rsp = TRUE;//拿到scan response if (p_i->inq_count != p_inq->inq_counter)
p_cur->device_type = BT_DEVICE_TYPE_BLE;//这次inquiry的第一次处理
else
p_cur->device_type |= BT_DEVICE_TYPE_BLE;//if(p_i->inq_count == p_inq->inq_counter) indicated that has been updated ,至少是BLE if (evt_type != BTM_BLE_SCAN_RSP_EVT)
p_cur->ble_evt_type = evt_type; p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ if (p_le_inq_cb->adv_len != )
{
if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_FLAG, &len)) != NULL)//读取flag
p_cur->flag = * p_flag;
} if (p_le_inq_cb->adv_len != )
{
/* Check to see the BLE device has the Appearance UUID in the advertising data. If it does
* then try to convert the appearance value to a class of device value Bluedroid can use.
* Otherwise fall back to trying to infer if it is a HID device based on the service class.
*/
p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_APPEARANCE, &len);
if (p_uuid16 && len == )
{
btm_ble_appearance_to_cod((UINT16)p_uuid16[] | (p_uuid16[] << ), p_cur->dev_class);
}
else
{
if ((p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache,
BTM_BLE_AD_TYPE_16SRV_CMPL, &len)) != NULL)
{
UINT8 i;
for (i = ; i + <= len; i = i + )
{
/* if this BLE device support HID over LE, set HID Major in class of device */
if ((p_uuid16[i] | (p_uuid16[i+] << )) == UUID_SERVCLASS_LE_HID)
{
p_cur->dev_class[] = ;
p_cur->dev_class[] = BTM_COD_MAJOR_PERIPHERAL;
p_cur->dev_class[] = ;
break;
}
}
}
}
} /* if BR/EDR not supported is not set, assume is a DUMO device */
if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == &&
evt_type != BTM_BLE_CONNECT_DIR_EVT)
{
if (p_cur->ble_addr_type != BLE_ADDR_RANDOM)
{
BTM_TRACE_DEBUG("BR/EDR NOT support bit not set, treat as DUMO");
p_cur->device_type |= BT_DEVICE_TYPE_DUMO;
} else {
BTM_TRACE_DEBUG("Random address, treating device as LE only");
}
}
else
{
BTM_TRACE_DEBUG("BR/EDR NOT SUPPORT bit set, LE only device");
}
return to_report;
}

这里主要就是更新设备的信息。读取adv 包中的 flag等值,判断这个包是否要上报,以及更新device type。另外会根据BTM_BLE_AD_TYPE_APPEARANCE = 0x19 来判断device class-->btm_ble_appearance_to_cod

3.btm_ble_is_discoverable(bda, evt_type, p)

/*******************************************************************************
**
** Function btm_ble_is_discoverable
**
** Description check ADV flag to make sure device is discoverable and match
** the search condition
**
** Parameters
**
** Returns void
**
*******************************************************************************/
UINT8 btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p)
{
UINT8 *p_flag, flag = , rt = ;
UINT8 data_len;
tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms;
tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; /* for observer, always "discoverable */
if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))//这里注意,如果是observation,那么总是可以发现的
rt |= BTM_BLE_OBS_RESULT; if (BTM_BLE_IS_SEL_CONN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity) &&
(evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_CONNECT_DIR_EVT))
rt |= BTM_BLE_SEL_CONN_RESULT; if (p_le_inq_cb->adv_len != )
{
if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache,
BTM_BLE_AD_TYPE_FLAG, &data_len)) != NULL)//读取标志位并且判断
{
flag = * p_flag; if ((btm_cb.btm_inq_vars.inq_active & BTM_BLE_GENERAL_INQUIRY) &&
(flag & (BTM_BLE_LIMIT_DISC_FLAG|BTM_BLE_GEN_DISC_FLAG)) != )
{
BTM_TRACE_DEBUG("Find Generable Discoverable device");
rt |= BTM_BLE_INQ_RESULT;
} else if (btm_cb.btm_inq_vars.inq_active & BTM_BLE_LIMITED_INQUIRY &&
(flag & BTM_BLE_LIMIT_DISC_FLAG) != )
{
BTM_TRACE_DEBUG("Find limited discoverable device");
rt |= BTM_BLE_INQ_RESULT;
}
}
}
return rt;
}

上面代码主要作用就是解析adv包中的标志位,然后做解析。并且决定是否要继续处理这个包,如果rt = 0 ,那么就不会继续处理这个包。这里注意如果是observation ,那么这个包总是可以发现的。

4.bta_dm_inq_results_cb

下面来分析这个 最重要的函数:

/*******************************************************************************
**
** Function bta_dm_inq_results_cb
**
** Description Inquiry results callback from BTM
**
** Returns void
**
*******************************************************************************/
static void bta_dm_inq_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir)
{ tBTA_DM_SEARCH result;//使用这个结果上报结果
tBTM_INQ_INFO *p_inq_info;
UINT16 service_class; bdcpy(result.inq_res.bd_addr, p_inq->remote_bd_addr);//填充address
memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN);//填充device class
BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class);
result.inq_res.is_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER)?TRUE:FALSE;
result.inq_res.rssi = p_inq->rssi;//rssi #if (BLE_INCLUDED == TRUE)
result.inq_res.ble_addr_type = p_inq->ble_addr_type;
result.inq_res.inq_result_type = p_inq->inq_result_type;
result.inq_res.device_type = p_inq->device_type;
result.inq_res.flag = p_inq->flag;//填充flag
#endif /* application will parse EIR to find out remote device name */
result.inq_res.p_eir = p_eir; if((p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr)) != NULL)
{
/* initialize remt_name_not_required to FALSE so that we get the name by default */
result.inq_res.remt_name_not_required = FALSE;
} if(bta_dm_search_cb.p_search_cback)//回调
bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result); if(p_inq_info)
{
/* application indicates if it knows the remote name, inside the callback
copy that to the inquiry data base*/
if(result.inq_res.remt_name_not_required)
p_inq_info->appl_knows_rem_name = TRUE;//如果application已经知道名字 } }

这里的逻辑也比较简单,构造了一个tBTA_DM_SEARCH来上报事件,上报的事件的函数就是bta_dm_search_cb.p_search_cback = bte_search_devices_evt,我们来看看其具体的实现:

/*******************************************************************************
**
** Function bte_search_devices_evt
**
** Description Switches context from BTE to BTIF for DM search events
**
** Returns void
**
*******************************************************************************/
static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
UINT16 param_len = ;
if (p_data)
param_len += sizeof(tBTA_DM_SEARCH);
/* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
switch (event)
{
case BTA_DM_INQ_RES_EVT:
{
if (p_data->inq_res.p_eir)
param_len += HCI_EXT_INQ_RESPONSE_LEN;
}
break;
...
/* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
if (event == BTA_DM_INQ_RES_EVT){
p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);//这里check data 里面是否有名字,如果有的话,设置result 的标志位,协议栈就不会去查询名字
}
btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
(param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
}

这里主要就是check 名字。然后transfer 到btif 线程去执行,我们继续看:

/******************************************************************************
**
** Function btif_dm_search_devices_evt
**
** Description Executes search devices callback events in btif context
**
** Returns void
**
******************************************************************************/
static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
{
tBTA_DM_SEARCH *p_search_data;
BTIF_TRACE_EVENT("%s event=%s", __FUNCTION__, dump_dm_search_event(event)); switch (event)
{
...
case BTA_DM_INQ_RES_EVT:
{
/* inquiry result */
UINT32 cod;
bt_bdname_t bdname;
bt_bdaddr_t bdaddr;
UINT8 remote_name_len;
tBTA_SERVICE_MASK services = ;
bdstr_t bdstr; p_search_data = (tBTA_DM_SEARCH *)p_param;//获得数据
bdcpy(bdaddr.address, p_search_data->inq_res.bd_addr);//获得地址 bdname.name[] = ; cod = devclass2uint (p_search_data->inq_res.dev_class);//cod if (!check_eir_remote_name(p_search_data, bdname.name, &remote_name_len))
check_cached_remote_name(p_search_data, bdname.name, &remote_name_len);//如果data里面没有名字,那么就查找cache里面的名字 /* Check EIR for remote name and services */
if (p_search_data->inq_res.p_eir)
{
BTA_GetEirService(p_search_data->inq_res.p_eir, &services);
BTIF_TRACE_DEBUG("%s()EIR BTA services = %08X", __FUNCTION__, (UINT32)services);
/* TODO: Get the service list and check to see which uuids we got and send it back to the client. */
} {
bt_property_t properties[];
bt_device_type_t dev_type;
uint32_t num_properties = ;
bt_status_t status;
int addr_type = ; memset(properties, , sizeof(properties));
/* BD_ADDR */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_BDADDR, sizeof(bdaddr), &bdaddr);//保存地址
num_properties++;
/* BD_NAME */
/* Don't send BDNAME if it is empty */
if (bdname.name[])
{
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_BDNAME,
strlen((char *)bdname.name), &bdname);//保存名字
num_properties++;
} /* DEV_CLASS */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod), &cod);//保存cod
num_properties++;
/* DEV_TYPE */
#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
/* FixMe: Assumption is that bluetooth.h and BTE enums match */ /* Verify if the device is dual mode in NVRAM */
int stored_device_type = ;
if (btif_get_device_type(bdaddr.address, &stored_device_type) &&
((stored_device_type == BT_DEVICE_TYPE_BLE &&
p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BREDR) ||
(stored_device_type == BT_DEVICE_TYPE_BREDR &&
p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE))) {
dev_type = BT_DEVICE_TYPE_DUMO;
} else {
dev_type = p_search_data->inq_res.device_type;
} if (p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE)
addr_type = p_search_data->inq_res.ble_addr_type;
#else
dev_type = BT_DEVICE_TYPE_BREDR;
#endif
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type), &dev_type);//保存dev type
num_properties++;
/* RSSI */
BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
BT_PROPERTY_REMOTE_RSSI, sizeof(int8_t),
&(p_search_data->inq_res.rssi));//保存rssi
num_properties++; status = btif_storage_add_remote_device(&bdaddr, num_properties, properties);//将各个属性保存在文件系统中 #if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
status = btif_storage_set_remote_addr_type(&bdaddr, addr_type);
ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote addr type (inquiry)", status);
#endif
/* Callback to notify upper layer of device */
HAL_CBACK(bt_hal_cbacks, device_found_cb,
num_properties, properties);//向上汇报
}
}
break;
...

这里注意btif_storage_add_remote_device 是将各个属性保存在系统的配置文件中。然后 通过HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties); 来上面五个属性:设备地址、设备名字、设备类、设备类型、设备rssi

协议栈对于ble 设备广播包的处理就分析到这里。

蓝牙inquiry流程之Advertising Report的更多相关文章

  1. 蓝牙inquiry流程之HCI_Inquiry_Result_With_RSSI和HCI Extended Inquiry Result处理

    首先介绍一下和inquiry的相关的流程. inquiry是从协议栈下发的一个HCI命令.其格式如下: 这里简单介绍下第二个参数,是inquiry的持续时间, 从上图看出 inquiry持续的时间是 ...

  2. 蓝牙inquiry流程之Inquiry Complete处理

    inquiry流程一般持续有12s多,当inquiry完成的时候,设备端会上报一个Event: Inquiry Complete 上来,那协议栈是如何把这个事件上传到应用层的呢?本篇文章来分析一下其具 ...

  3. activiti自定义流程之Spring整合activiti-modeler5.16实例(九):历史任务查询

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  4. activiti自定义流程之Spring整合activiti-modeler5.16实例(八):完成个人任务

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  5. activiti自定义流程之Spring整合activiti-modeler5.16实例(七):任务列表展示

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  6. activiti自定义流程之Spring整合activiti-modeler5.16实例(六):启动流程

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  7. activiti自定义流程之Spring整合activiti-modeler5.16实例(五):流程定义列表

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  8. activiti自定义流程之Spring整合activiti-modeler5.16实例(四):部署流程定义

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

  9. activiti自定义流程之Spring整合activiti-modeler5.16实例(三):流程模型列表展示

    注:(1)环境搭建:activiti自定义流程之Spring整合activiti-modeler5.16实例(一):环境搭建        (2)创建流程模型:activiti自定义流程之Spring ...

随机推荐

  1. 安卓ADB命令

    查看连接的设备 adb devices -l FastBoot常用命令: fastboot erase system    #擦除system分区 fastboot erase boot    #擦除 ...

  2. Sql server的Merge语句,源表中如果有重复数据会导致执行报错

    用过sql server的Merge语句的开发人员都应该很清楚Merge用来做表数据的插入/更新是非常方便的,但是其中有一个问题值得关注,那就是Merge语句中的源表中不能出现重复的数据,我们举例来说 ...

  3. Jboss7.1 local EJB lookup problem

    We are trying to lookup for an Local EJB in JBoss7.1, but we get an ClassCast Exception. This local ...

  4. WEB 报表导入导出操作

    /** * 报表导出 * @param response */ @RequestMapping("/stuExcel") @LogAnno(value="对学生数据进行了 ...

  5. 关联与下钻:快速定位MySQL性能瓶颈的制胜手段

    本文根据DBAplus社群[2018年1月6日北京开源与架构技术沙龙]现场演讲内容整理而成. 讲师介绍 李季鹏 新炬网络数据库专家 专注于MySQL数据库性能管理及相关解决方案,目前主要从事MySQL ...

  6. MySQL索引原理以及类型

    1.什么是索引 索引是在MySQL的存储引擎上,对其表中的某个列或多列通过一些算法实现可快速查询出结果的一种方法. 2.为什么要有索引 就像一本书要有目录一样,我们可快速通过目录来查找对应的章节得出结 ...

  7. js常见执行方法window.onload = function (){},$(document).ready()

    1. window.onload = function(){}; 当页面DOM对象加载完毕,web浏览器能够运行JS时,此方法即被触发. 2. $(document).ready();当web页面以及 ...

  8. Springboot整合log4j2日志全解

    目录 常用日志框架 日志门面slf4j 为什么选用log4j2 整合步骤 引入Jar包 配置文件 配置文件模版 配置参数简介 Log4j2配置详解 简单使用 使用lombok工具简化创建Logger类 ...

  9. Python接口自动化--post提交的四种数据类型 4

    常见的post请求提交的数据类型有四种: 1.第一种:application/json:这是最常见的json格式,如下 {"input1":"XXX",&quo ...

  10. 死磕nginx系列--使用nginx做cache服务

    配置文件 nginx.conf 主配置文件 worker_processes 1; events { worker_connections 1024; } http { include mime.ty ...