Android BlueDroid 分析之扫描
来源:互联网 发布:两台mac之间远程协助 编辑:程序博客网 时间:2024/05/08 07:32
这段时间一直在跟一个BLE相关的项目,之前对BLE基本没怎么接触,所以刚好趁这个机会好好把bluedroid好好梳理一遍。要完全分析清楚估计得花老长时间了,先一步步来吧,
- BlueDroid 代码结构
- BlueDroid 扫描的调用流程
- BlueDroid 的一些重要数据结构
BlueDroid 代码结构
先看下Android 里BlueDroid里的代码结构:
很多目录吧,究竟从哪里看起很头疼吧。。
BlueDroid的入口在main目录中的bte_main.c中:
void bte_main_enable(){ APPL_TRACE_DEBUG("%s", __FUNCTION__); module_start_up(get_module(BTSNOOP_MODULE)); module_start_up(get_module(HCI_MODULE)); BTU_StartUp();}
所有的启动都在后续进行。具体的初始化啥,启动啥,需要自己去研究咯。为了更熟悉bluedroid的代码,一般建议从一些特定的场景开始入手,这次我从BLE常见的扫描到连接这样一个使用场景先来看看bluedroid的代码结构。
BlueDroid 的一般调用流程
如上面所讲的,我们现在从BLE常见的扫描与连接开始这一流程,强调下,我们在这里只关注与bluedroid层面,不关心Android Application 与Android Framework的实现,因为这样的介绍已经有很多了。CSDN上不少博客都有介绍,就不在这里梳理了。
BLE 设备扫描流程
Android Application在上层调用 startLeScan()来启动扫描流程,我们关注下BlueDroid里如何来响应这样一个扫描流程。
BlueDroid里提供给上层的接口:
static const btgatt_interface_t btgattInterface = { sizeof(btgattInterface), btif_gatt_init, btif_gatt_cleanup, &btgattClientInterface, &btgattServerInterface,};
这个btgattInterface的接口就是提供给Android framework使用的,两个接口函数,一个初始化函数,一个清理函数。另外就是两个接口指针,一个给GattClient调用,一个给GattServer调用。 先只关注GattClient的接口吧。
const btgatt_client_interface_t btgattClientInterface = { btif_gattc_register_app, btif_gattc_unregister_app, btif_gattc_scan, btif_gattc_open, btif_gattc_close, 。。。。。。。。。 。。。。。。。。。 }
这个接口里定义函数很多,先只摘录一小部分。btif_gattc_scan 会最终响应上层的startLeScan函数,为什么呢?自己起找对应关系吧,Android里framework分析的基本功哈。
接下来流程会是这样的:
BTIF btif_gattc_scan BTIF_GATTC_SCAN_STARTBTA BTA_DmBleObserve bta_dm_ble_observeBTM BTM_BleObserve btm_ble_start_scanHCI btsnd_hcic_ble_set_scan_enable
上面左边代表bluedroid里的逻辑分层,右边代表是在每个逻辑层具体走过的函数。等下我们过下具体的函数逻辑。从左边来看逻辑上bluedroid上可以分成BTIF,BTA,BTM,HCI四个层次,但是其实应该还可以加上BTU等层,从字面上看BTIF是接口层,具体应该就是与Android之间的接口,BTA指应用层,BTM指Bluedroid中的管理层,HCI是指HOST Control Interface,即主机与BT控制器之间的接口。
我们来一个个过下这上面提到的函数的一些重要逻辑:
前面的btif_gattc_scan与BTA_DmBleObserve都没啥好说的。都是简单的把事件包装往下发送。
bta_dm_ble_observe:
void bta_dm_ble_observe (tBTA_DM_MSG *p_data){ tBTM_STATUS status; if (p_data->ble_observe.start) { /*Save the callback to be called when a scan results are available */ bta_dm_search_cb.p_scan_cback = p_data->ble_observe.p_cback; if ((status = BTM_BleObserve(TRUE, p_data->ble_observe.duration, bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb))!= BTM_CMD_STARTED) { tBTA_DM_SEARCH data; APPL_TRACE_WARNING(" %s BTM_BleObserve failed. status %d",__FUNCTION__,status); data.inq_cmpl.num_resps = 0; if (bta_dm_search_cb.p_scan_cback) { bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_CMPL_EVT, &data); } } } else { bta_dm_search_cb.p_scan_cback = NULL; BTM_BleObserve(FALSE, 0, NULL,NULL ); }}
这里会设置一些callback回调供返回扫描结果。
首先是将从上层设置下来的callback函数指针保存到bta_dm_search_cb这么个全局变量中:
bta_dm_search_cb.p_scan_cback = p_data->ble_observe.p_cback;
然后设置BTA的callback函数往下调用:
BTM_BleObserve(TRUE, p_data->ble_observe.duration, bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb)
bta_dm_observe_results_cb与bta_dm_observe_cmpl_cb这两个回调函数从函数命名上看也知道是一个返回实时扫描结果,一个返回扫描完成。两个回调分别使用BTA_DM_INQ_RES_EVT与BTA_DM_INQ_CMPL_EVT作为参数回调bta_dm_search_cb.p_scan_cback 。
需要注意下bta_dm_search_cb这个全局对象,其是一个名叫tBTA_DM_SEARCH_CB的结构体实例
/* DM search control block */typedef struct{ tBTA_DM_SEARCH_CBACK * p_search_cback; tBTM_INQ_INFO * p_btm_inq_info; tBTA_SERVICE_MASK services; 。。。。。。。。。。。。。。。 tBTA_TRANSPORT transport;#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) tBTA_DM_SEARCH_CBACK * p_scan_cback;#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE)) tBTA_GATTC_IF client_if; UINT8 num_uuid; tBT_UUID *p_srvc_uuid; UINT8 uuid_to_search; 。。。。。。。。。 TIMER_LIST_ENT gatt_close_timer; /* GATT channel close delay timer */ BD_ADDR pending_close_bda; /* pending GATT channel remote device address */#endif#endif} tBTA_DM_SEARCH_CB;
暂时还没用到太多的成员,只有tBTA_DM_SEARCH_CBACK * p_scan_cback;
BTM_BleObserve实现也比较简单:
将上层传递过来的回调函数设置到一个新的结构体实例btm_cb当中。这个结构体非常重要哈,在bluedroid里应用得非常广泛。
btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb; btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb;
配置扫描参数,我自己的nexus 5手机显示不支持extended_scan_support:
if (cmn_ble_vsc_cb.extended_scan_support == 0) { btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, (UINT16)scan_window, btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, BTM_BLE_DEFAULT_SFP); } else { btm_ble_send_extended_scan_params(p_inq->scan_type, scan_interval, scan_window, btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, BTM_BLE_DEFAULT_SFP); }
继续往下走:
status = btm_ble_start_scan();
发送HCI命令
/* start scan, disable duplicate filtering */ if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter)) { status = BTM_NO_RESOURCES; }BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate){ BT_HDR *p; UINT8 *pp; if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE)) == NULL) return (FALSE); pp = (UINT8 *)(p + 1); p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE; p->offset = 0; UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_ENABLE); UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE); UINT8_TO_STREAM (pp, scan_enable); UINT8_TO_STREAM (pp, duplicate); btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); return (TRUE);}
好了,扫描命令有发送出去了。等着HCI告诉你结果吧。
扫描结果返回
既然我们的扫描命令是通过HCI送进bt 控制器的,那么理所当然扫描的结果也应该是HCI返回回来的。
BTU btu_hcif_process_event/HCI_BLE_ADV_PKT_RPT_EVT btu_ble_process_adv_pktBTM btm_ble_process_adv_pkt btm_ble_process_adv_pkt_contBTA bta_scan_results_cbBTIF BTIF_GATT_OBSERVE_EVT
从上面看起来,事件是通过HCI–>BTU—>BTM—->BTA—->BTIF一层层往上抛的,刚好与发送扫描命令相反。
重点看下btm_ble_process_adv_pkt_cont函数:
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; p_i = btm_inq_db_find (bda); /* Check if this address has already been processed for this inquiry */ if (btm_inq_find_bdaddr(bda))
btm_inq_db_find 送数据库里寻找是否已经存在有同样地址的设备。所谓的数据开也就是:btm_cb.btm_inq_vars,前面说过btm_cb基本无处不在哈。
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;
这里拿到的回调就是我们之前看到的呀。
这个函数的最主要逻辑就是将新扫描到的设备放进扫描数据库里,也就是btm_cb.btm_inq_vars中。然后回调通知上层扫描结果。
static void bta_scan_results_cb (tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data){ btif_gattc_cb_t btif_cb; uint8_t len; switch (event) { case BTA_DM_INQ_RES_EVT: { bdcpy(btif_cb.bd_addr.address, p_data->inq_res.bd_addr); btif_cb.device_type = p_data->inq_res.device_type; btif_cb.rssi = p_data->inq_res.rssi; btif_cb.addr_type = p_data->inq_res.ble_addr_type; btif_cb.flag = p_data->inq_res.flag; if (p_data->inq_res.p_eir) { memcpy(btif_cb.value, p_data->inq_res.p_eir, 62); if (BTM_CheckEirData(p_data->inq_res.p_eir, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, &len)) { p_data->inq_res.remt_name_not_required = TRUE; } } } break; case BTA_DM_INQ_CMPL_EVT: { BTIF_TRACE_DEBUG("%s BLE observe complete. Num Resp %d", __FUNCTION__,p_data->inq_cmpl.num_resps); return; } default: BTIF_TRACE_WARNING("%s : Unknown event 0x%x", __FUNCTION__, event); return; } btif_transfer_context(btif_gattc_upstreams_evt, BTIF_GATT_OBSERVE_EVT, (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);}
这个函数也没干太多事,把结果往BTIF里送:
case BTIF_GATT_OBSERVE_EVT: { btif_gattc_cb_t *p_btif_cb = (btif_gattc_cb_t*) p_param; uint8_t remote_name_len; uint8_t *p_eir_remote_name=NULL; bt_device_type_t dev_type; bt_property_t properties; p_eir_remote_name = BTM_CheckEirData(p_btif_cb->value, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, &remote_name_len); if (p_eir_remote_name == NULL) { p_eir_remote_name = BTM_CheckEirData(p_btif_cb->value, BT_EIR_SHORTENED_LOCAL_NAME_TYPE, &remote_name_len); } if ((p_btif_cb->addr_type != BLE_ADDR_RANDOM) || (p_eir_remote_name)) { if (!btif_gattc_find_bdaddr(p_btif_cb->bd_addr.address)) { btif_gattc_add_remote_bdaddr(p_btif_cb->bd_addr.address, p_btif_cb->addr_type); btif_gattc_update_properties(p_btif_cb); } } dev_type = p_btif_cb->device_type; BTIF_STORAGE_FILL_PROPERTY(&properties, BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type), &dev_type); btif_storage_set_remote_device_property(&(p_btif_cb->bd_addr), &properties); btif_storage_set_remote_addr_type( &p_btif_cb->bd_addr, p_btif_cb->addr_type); HAL_CBACK(bt_gatt_callbacks, client->scan_result_cb, &p_btif_cb->bd_addr, p_btif_cb->rssi, p_btif_cb->value); break; }
BTIF里也没干啥事,就存起来,再通知上层应用。至此一个简单的扫描返回结果的流程就算结束了。
BlueDroid 的一些重要数据结构
在这里,我们就碰到两个结构比较重要:
tBTA_DM_SEARCH_CB 与tBTM_CB btm_cb;一个在BTA层,一个在BTM层,这里虽然带后缀是cb,但应该是control block的意思,不是call back的意思哈。尤其是tBTM_CB btm_cb;后续打交道的日子还多着呢。
一些简单总结
BTIF —> BTA —-> BTM —–> BTU —–> HCI
bluedroid的代码层级基本是这么定义,后续可能再从其他角度来看下这个层级结构。
- Android BlueDroid 分析之扫描
- BlueDroid代码分析之BlueDroid简介
- BlueDroid代码分析之GKI
- bluedroid底层分析之GKI
- BlueDroid代码分析之初始化
- BlueDroid代码分析之L2CAP
- BlueDroid代码分析之GKI
- BlueDroid代码分析之GKI
- BlueDroid代码分析之GKI
- BlueDroid代码分析之数据传输过程
- Android BlueDroid分析: Linux中的Eventfd
- android蓝牙框架bluedroid之sbc编码
- Android BlueDroid分析: OSI中的HashMap的实现
- BlueDroid代码分析
- BlueDroid代码分析
- Bluedroid GKI源码分析
- Android BlueDroid(一):BlueDroid概述
- Android BlueDroid(一):BlueDroid概述
- Miracast/RTSP
- 51nod 1165 整边直角三角形的数量 【数学:公式--求约数】
- C语言指向多维数组的指针
- 数据集预处理,划分为测试数据集合验证数据集
- Sicily ISBN
- Android BlueDroid 分析之扫描
- Codeforces 741A (求环的大小)
- freetype的安装与使用
- Node.js实现简单的mysql数据库构造函数, 主要是语句生成
- win32API实现透明字幕窗口
- 100个G以上的超大文件的下载方法
- BroadcastReceiver广播接收者的两种注册方式
- Mac下切换zsh和bash
- truncate有外键约束的表,报ORA-02266处理。