本文出自:《蓝牙BLE设备主机重启回连流程分析》

如果一个BLE设备已经与蓝牙中心设备连接上,那么当中心设备的断电重启,其依然会和配对过的BLE设备连接上,而不需要重新走配对的流程,这个过程叫做回连。

这篇文章就分析一下当中心设备断电重启之后,其与BLE设备的回连的流程。

当设备重启之后,蓝牙协议栈以及所有的上层的profile 都要重新进行初始化,
之前的配对信息是保存在文件bt_config.conf中,蓝牙起来之后,会去加载这个文件,去解析曾经配对过的设备,
对于已经配对过的设备,并且配对信息保持完整,那么就会对该设备发起回连。

那么是什么时候进行对bt_config.conf文件的解析的工作的呢?

蓝牙初始化结束之后,会接着对于各个profile的初始化,当Hid host profile 打开完成之后会对bt_config.conf 文件进行解析并 加载相应的设备信息。我们这里从hid host 打开完成之后开始分析:

hid host 打开完成之后,会经过btif_hh_upstreams_evt 来上报消息:

这篇文章就分析一下当中心设备断电重启之后,其与BLE设备的回连的流程。

当设备重启之后,蓝牙协议栈以及所有的上层的profile 都要重新进行初始化,
之前的配对信息是保存在文件bt_config.conf中,蓝牙起来之后,会去加载这个文件,去解析曾经配对过的设备,
对于已经配对过的设备,并且配对信息保持完整,那么就会对该设备发起回连。

那么是什么时候进行对bt_config.conf文件的解析的工作的呢?

蓝牙初始化结束之后,会

static void btif_hh_upstreams_evt(UINT16 event, char* p_param)
{tBTA_HH *p_data = (tBTA_HH *)p_param;btif_hh_device_t *p_dev = NULL;int i;int len, tmplen;BTIF_TRACE_DEBUG("%s: event=%s", __FUNCTION__, dump_hh_event(event));switch (event){case BTA_HH_ENABLE_EVT:BTIF_TRACE_DEBUG("%s: BTA_HH_ENABLE_EVT: status =%d",__FUNCTION__, p_data->status);if (p_data->status == BTA_HH_OK) {btif_hh_cb.status = BTIF_HH_ENABLED;BTIF_TRACE_DEBUG("%s--Loading added devices",__FUNCTION__);/* Add hid descriptors for already bonded hid devices*/btif_storage_load_bonded_hid_info();/*load hid information*/}
...

我们继续看btif_storage_load_bonded_hid_info 的实现:

/*******************************************************************************
**
** Function         btif_storage_load_bonded_hid_info
**
** Description      BTIF storage API - Loads hid info for all the bonded devices from NVRAM
**                  and adds those devices  to the BTA_HH.
**
** Returns          BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise
**
*******************************************************************************/
bt_status_t btif_storage_load_bonded_hid_info(void)
{bt_bdaddr_t bd_addr;tBTA_HH_DEV_DSCP_INFO dscp_info;uint16_t attr_mask;uint8_t  sub_class;uint8_t  app_id;memset(&dscp_info, 0, sizeof(dscp_info));for (const btif_config_section_iter_t *iter = btif_config_section_begin(); iter != btif_config_section_end(); iter = btif_config_section_next(iter)) {const char *name = btif_config_section_name(iter);if (!string_is_bdaddr(name))continue;BTIF_TRACE_DEBUG("Remote device:%s", name);int value;if(btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS)//获取bonded devices信息{if(btif_config_get_int(name, "HidAttrMask", &value))//保存各种属性{attr_mask = (uint16_t)value;btif_config_get_int(name, "HidSubClass", &value);sub_class = (uint8_t)value;btif_config_get_int(name, "HidAppId", &value);app_id = (uint8_t)value;btif_config_get_int(name, "HidVendorId", &value);dscp_info.vendor_id = (uint16_t) value;btif_config_get_int(name, "HidProductId", &value);dscp_info.product_id = (uint16_t) value;btif_config_get_int(name, "HidVersion", &value);dscp_info.version = (uint8_t) value;btif_config_get_int(name, "HidCountryCode", &value);dscp_info.ctry_code = (uint8_t) value;value = 0;btif_config_get_int(name, "HidSSRMaxLatency", &value);dscp_info.ssr_max_latency = (uint16_t) value;value = 0;btif_config_get_int(name, "HidSSRMinTimeout", &value);dscp_info.ssr_min_tout = (uint16_t) value;size_t len = btif_config_get_bin_length(name, "HidDescriptor");if(len > 0){dscp_info.descriptor.dl_len = (uint16_t)len;dscp_info.descriptor.dsc_list = (uint8_t*)alloca(len);btif_config_get_bin(name, "HidDescriptor", (uint8_t *)dscp_info.descriptor.dsc_list, &len);}string_to_bdaddr(name, &bd_addr);// add extracted information to BTA HHif (btif_hh_add_added_dev(bd_addr,attr_mask))//addd new devices to btif_hh_cb.added_devices{BTA_HhAddDev(bd_addr.address, attr_mask, sub_class,app_id, dscp_info);//add device to BTA_HH}}}else{...}}return BT_STATUS_SUCCESS;
}

上面的流程很简单,获取已经绑定的设备各种信息,主要有哪些信息呢?这里附上已经绑定设备的保存的信息(BLE):

[d0:09:09:dc:00:59]
DevClass = 1344
AddrType = 0
Name = RCU
DevType = 2
LE_KEY_PENC = 23e2e46363ca4ca562dadc625cc244a41f2225d81a0a8d1924e80110
LE_KEY_PID = 0000000000000000000000000000000000d00909dc0059
LE_KEY_PCSRK = 0000000043210c0c420203040505080708090c0101c674e3
LE_KEY_LENC = 09d6fa5537cb5859f7f9220613104ab0a2f11001
LE_KEY_LCSRK = 00000000a2f10122d4ecbea1a40287b6eb74c08767855ce2
LE_KEY_LID =
Service = 00001812-0000-1000-8000-00805f9b34fb
HidReport = 4d2a0201001a4d2a0101011a4d2a0102020e4d2afd01031a4d2a5a01041a4d2a5a02050e
HidAttrMask = 0
HidSubClass = 0
HidAppId = 255
HidVendorId = 93
HidProductId = 2
HidVersion = 0
HidCountryCode = 0
HidSSRMaxLatency = 0
HidSSRMinTimeout = 0
HidDescriptor = 05010906a1018501050719e029e71500250175019508810295017508810195057501050819012905910295017503910195067508150025a40507190029a48100c005010902a10185020901a1000509190129031500250195037501810295017505810105010930093109381581257f750895038106c0c00612ff0a12ffa10185fd0901750895ff16000026ff00190029ff8100c00612ff0a12ffa101855a0901750895ff16000026ff00190029ff8100950875010508190129089102c0

上面的主要流程就是解析文件,确认某个设备的确是已经绑定好的设备,
那么接下来就要走回连的流程,
将该设备加入到白名单中,以及发起相应的连接流程,
该流程的入口函数就是BTA_HhAddDev,

下面我们继续分析该函数:

/*******************************************************************************
**
** Function         BTA_HhAddDev
**
** Description      Add a virtually cabled device into HID-Host device list
**                  to manage and assign a device handle for future API call,
**                  host applciation call this API at start-up to initialize its
**                  virtually cabled devices.
**
** Returns          void
**
*******************************************************************************/
void BTA_HhAddDev(BD_ADDR bda, tBTA_HH_ATTR_MASK attr_mask, UINT8 sub_class,UINT8 app_id, tBTA_HH_DEV_DSCP_INFO dscp_info)
{tBTA_HH_MAINT_DEV    *p_buf;UINT16  len = sizeof(tBTA_HH_MAINT_DEV) + dscp_info.descriptor.dl_len;p_buf = (tBTA_HH_MAINT_DEV *)GKI_getbuf(len);if (p_buf != NULL){memset(p_buf, 0, sizeof(tBTA_HH_MAINT_DEV));p_buf->hdr.event            = BTA_HH_API_MAINT_DEV_EVT;//发送此eventp_buf->sub_event            = BTA_HH_ADD_DEV_EVT;p_buf->hdr.layer_specific   = BTA_HH_INVALID_HANDLE;p_buf->attr_mask            = (UINT16) attr_mask;p_buf->sub_class            = sub_class;p_buf->app_id               = app_id;bdcpy(p_buf->bda, bda);memcpy(&p_buf->dscp_info, &dscp_info, sizeof(tBTA_HH_DEV_DSCP_INFO));if ( dscp_info.descriptor.dl_len != 0 && dscp_info.descriptor.dsc_list){p_buf->dscp_info.descriptor.dl_len =  dscp_info.descriptor.dl_len;p_buf->dscp_info.descriptor.dsc_list = (UINT8 *)(p_buf + 1);memcpy(p_buf->dscp_info.descriptor.dsc_list, dscp_info.descriptor.dsc_list, dscp_info.descriptor.dl_len);}else{...}bta_sys_sendmsg(p_buf);//发送消息}
}

向BTU task 发送了BTA_HH_API_MAINT_DEV_EVT,其中包含的数据部分 都是 上面从文件解析出来的。
我们继续看BTA_HH_API_MAINT_DEV_EVT 的处理,关于bta_sys_sendmsg 消息传送机制,这里就不讲了。

简单看下消息流向:

/*******************************************************************************
**
** Function         bta_hh_hdl_event
**
** Description      HID host main event handling function.
**
**
** Returns          void
**
*******************************************************************************/
BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg)
{UINT8           index = BTA_HH_IDX_INVALID;tBTA_HH_DEV_CB *p_cb = NULL;switch (p_msg->event){...default:/* all events processed in state machine need to find correspondingCB before proceed */if (p_msg->event == BTA_HH_API_OPEN_EVT){index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr);}else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT)//add{/* if add device */if (((tBTA_HH_MAINT_DEV *)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT){index = bta_hh_find_cb(((tBTA_HH_MAINT_DEV *)p_msg)->bda);//find devive control block}
...
p_cb = &bta_hh_cb.kdev[index];
...
bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg);//进入状态机

我们看看状态机的轮转情况:

/*******************************************************************************
**
** Function         bta_hh_sm_execute
**
** Description      State machine event handling function for HID Host
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data)
{tBTA_HH_ST_TBL  state_table;UINT8           action;tBTA_HH         cback_data;tBTA_HH_EVT     cback_event = 0;memset(&cback_data, 0, sizeof(tBTA_HH));
...
{...state_table = bta_hh_st_tbl[p_cb->state - 1];event &= 0xff;p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ;if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE){(*bta_hh_action[action])(p_cb, p_data);}
...}return;
}

这里的状态机轮转也是 熟悉的套路,开始是 idle状态,bta_hh_st_idle

/* BTA_HH_API_MAINT_DEV_EVT */    {BTA_HH_MAINT_DEV_ACT, BTA_HH_IDLE_ST    },

事件处理之后还是idle 状态,执行的行为是BTA_HH_MAINT_DEV_ACT:

/*******************************************************************************
**
** Function         bta_hh_maint_dev_act
**
** Description      HID Host maintain device list.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
{tBTA_HH_MAINT_DEV       *p_dev_info = &p_data->api_maintdev;tBTA_HH_DEV_INFO        dev_info ;UINT8                   dev_handle;dev_info.status = BTA_HH_ERR;dev_info.handle = BTA_HH_INVALID_HANDLE;switch (p_dev_info->sub_event){case BTA_HH_ADD_DEV_EVT:    /* add a device */bdcpy(dev_info.bda, p_dev_info->bda);/* initialize callback data */if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE){#if (BTA_HH_LE_INCLUDED == TRUE)if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)){dev_info.handle   = bta_hh_le_add_device(p_cb, p_dev_info);dev_info.status   = BTA_HH_OK;}else
#endifif (HID_HostAddDev(p_dev_info->bda, p_dev_info->attr_mask, &dev_handle)\== HID_SUCCESS)... /*非LE部分处理*/}
...
(* bta_hh_cb.p_cback)(p_dev_info->sub_event, (tBTA_HH *)&dev_info);//bte_hh_evt--->btif_hh_upstreams_evt

这里涉及到的回调函数就是bte_hh_evt,其实他最终还是调用btif_hh_upstreams_evt 来完成功能。
我们看看其实现:

case BTA_HH_ADD_DEV_EVT:BTIF_TRACE_WARNING("BTA_HH_ADD_DEV_EVT: status = %d, handle = %d",p_data->dev_info.status, p_data->dev_info.handle);int i;for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) {if (memcmp(btif_hh_cb.added_devices[i].bd_addr.address, p_data->dev_info.bda, 6) == 0) {if (p_data->dev_info.status == BTA_HH_OK) {btif_hh_cb.added_devices[i].dev_handle = p_data->dev_info.handle;/*其实就是保存dev_handle*/}else {/*错误处理*/}break;}}break;

这个handle 就是我们上面看到的

 dev_info.handle   = bta_hh_le_add_device(p_cb, p_dev_info);

我们继续看看其干了什么?
我们先看看他的注释怎么说

/*******************************************************************************
**
** Function         bta_hh_le_add_device
**
** Description      Add a LE HID device as a known device, and also add the address
**                  into back ground connection WL for incoming connection.
**
** Returns          void
**
*******************************************************************************/

添加 该设备地址到whitelist 里面,当设备科连接的时候,就会直接连接上。

UINT8 bta_hh_le_add_device(tBTA_HH_DEV_CB *p_cb, tBTA_HH_MAINT_DEV *p_dev_info)
{p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index);bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index;/* update DI information */bta_hh_update_di_info(p_cb,p_dev_info->dscp_info.vendor_id,p_dev_info->dscp_info.product_id,p_dev_info->dscp_info.version,p_dev_info->dscp_info.flag);/*更新设备消息,这些消息都是从bt_config.conf里面load的*//* add to BTA device list */bta_hh_add_device_to_list(p_cb, p_cb->hid_handle,p_dev_info->attr_mask,&p_dev_info->dscp_info.descriptor,p_dev_info->sub_class,p_dev_info->dscp_info.ssr_max_latency,p_dev_info->dscp_info.ssr_min_tout,p_dev_info->app_id);//添加dscp_info 等消息到p_cbbta_hh_le_add_dev_bg_conn(p_cb, FALSE);/*add device to WL*/return p_cb->hid_handle;
}

上面代码的核心就是 添加 设备到BG connection,我们具体看实现:

static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond)
{UINT8           sec_flag=0;BOOLEAN         to_add = TRUE;if (check_bond){...}if (/*p_cb->dscp_info.flag & BTA_HH_LE_NORMAL_CONN &&*/!p_cb->in_bg_conn && to_add){/* add device into BG connection to accept remote initiated connection */BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE, BTA_GATT_TRANSPORT_LE);//gatt_if for hogp p_cb->in_bg_conn = TRUE;BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL);//send msg the auto type}return;
}

这里分为两个部分来分析:

 1. BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE, BTA_GATT_TRANSPORT_LE);添加设备,并且标志为自动连接2. BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL); 开始回连的操作

1. BTA_GATTC_OPen的实现

我们先看BTA_GATTC_OPen的实现,之前其实有一篇文章讲这个,
但是那篇文章主要分析是direct connect,而这里主要分析auto connect:

void BTA_GATTC_Open(tBTA_GATTC_IF client_if, BD_ADDR remote_bda,BOOLEAN is_direct, tBTA_GATT_TRANSPORT transport)
{tBTA_GATTC_API_OPEN  *p_buf;if ((p_buf = (tBTA_GATTC_API_OPEN *) GKI_getbuf(sizeof(tBTA_GATTC_API_OPEN))) != NULL){p_buf->hdr.event = BTA_GATTC_API_OPEN_EVT;p_buf->client_if = client_if;p_buf->is_direct = is_direct;//falsep_buf->transport = transport;//LEmemcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN);bta_sys_sendmsg(p_buf);}return;
}

看看event的处理:

/*******************************************************************************
**
** Function         bta_gattc_hdl_event
**
** Description      GATT client main event handling function.
**
**
** Returns          BOOLEAN
**
*******************************************************************************/
BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{...case BTA_GATTC_API_OPEN_EVT:bta_gattc_process_api_open(p_cb, (tBTA_GATTC_DATA *) p_msg);break;
...

看看 关于 auto connection的处理:

/*******************************************************************************
**
** Function         bta_gattc_process_api_open
**
** Description      process connect API request.
**
** Returns          void
**
*******************************************************************************/
void bta_gattc_process_api_open (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA * p_msg)
{UINT16 event = ((BT_HDR *)p_msg)->event;tBTA_GATTC_CLCB *p_clcb = NULL;tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_conn.client_if);//r is register UNUSED(p_cb);if (p_clreg != NULL){if (p_msg->api_conn.is_direct){/*直连的处理*/}else{bta_gattc_init_bk_conn(&p_msg->api_conn, p_clreg);}}}

我们只要是看看 background connection的情况:

/*******************************************************************************
**
** Function         bta_gattc_init_bk_conn
**
** Description      Process API Open for a background connection
**
** Returns          void
**
*******************************************************************************/
void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg)
{tBTA_GATT_STATUS         status = BTA_GATT_NO_RESOURCES;UINT16                   conn_id;tBTA_GATTC_CLCB         *p_clcb;tBTA_GATTC_DATA         gattc_data;if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, TRUE, FALSE))//标记{/* always call open to hold a connection */if (!GATT_Connect(p_data->client_if, p_data->remote_bda, FALSE, p_data->transport))//gatt 连接,auto connection{uint8_t *bda = (uint8_t *)p_data->remote_bda;status = BTA_GATT_ERROR;}else{status = BTA_GATT_OK;/* if is a connected remote device */if (GATT_GetConnIdIfConnected(p_data->client_if,p_data->remote_bda,&conn_id,p_data->transport))//走到这里一般只是下发hci的连接命令,并没有连接上{/*l连接上的处理*/}}}
...
}

我们看看GATT_Connect 的处理:

/*******************************************************************************
**
** Function         GATT_Connect
**
** Description      This function initiate a connecttion to a remote device on GATT
**                  channel.
**
** Parameters       gatt_if: applicaiton interface
**                  bd_addr: peer device address.
**                  is_direct: is a direct conenection or a background auto connection
**
** Returns          TRUE if connection started; FALSE if connection start failure.
**
*******************************************************************************/
BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct, tBT_TRANSPORT transport)
{tGATT_REG    *p_reg;BOOLEAN status = FALSE;GATT_TRACE_API ("GATT_Connect gatt_if=%d", gatt_if);/* Make sure app is registered */if ((p_reg = gatt_get_regcb(gatt_if)) == NULL){GATT_TRACE_ERROR("GATT_Connect - gatt_if =%d is not registered", gatt_if);return(FALSE);}if (is_direct)status = gatt_act_connect (p_reg, bd_addr, transport);else{if (transport == BT_TRANSPORT_LE)status = gatt_update_auto_connect_dev(gatt_if,TRUE, bd_addr, TRUE);//update auto connection 相关else{GATT_TRACE_ERROR("Unsupported transport for background connection");}}return status;}

我们继续看:

/*******************************************************************************
**
** Function         gatt_update_auto_connect_dev
**
** Description      This function add or remove a device for background connection
**                  procedure.
**
** Parameters       gatt_if: Application ID.
**                  add: add peer device
**                  bd_addr: peer device address.
**
** Returns          TRUE if connection started; FALSE if connection start failure.
**
*******************************************************************************/
BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr, BOOLEAN is_initator)
{BOOLEAN         ret = FALSE;tGATT_REG        *p_reg;tGATT_TCB       *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE);GATT_TRACE_API ("gatt_update_auto_connect_dev ");/* Make sure app is registered */if ((p_reg = gatt_get_regcb(gatt_if)) == NULL){GATT_TRACE_ERROR("gatt_update_auto_connect_dev - gatt_if is not registered", gatt_if);return(FALSE);}if (add){ret = gatt_add_bg_dev_list(p_reg, bd_addr, is_initator);/*加入到bg connection*/if (ret && p_tcb != NULL)/*如果是一个已经连接的设备*/{/* if a connected device, update the link holding number */gatt_update_app_use_link_flag(gatt_if, p_tcb, TRUE, TRUE);}}else{ret = gatt_remove_bg_dev_from_list(p_reg, bd_addr, is_initator);}return ret;
}

我们继续看gatt_add_bg_dev_list的实现:

/*******************************************************************************
**
** Function         gatt_add_bg_dev_list
**
** Description      add/remove device from the back ground connection device list
**
** Returns          TRUE if device added to the list; FALSE failed
**
*******************************************************************************/
BOOLEAN gatt_add_bg_dev_list(tGATT_REG *p_reg,  BD_ADDR bd_addr, BOOLEAN is_initator)
{tGATT_IF gatt_if =  p_reg->gatt_if;tGATT_BG_CONN_DEV   *p_dev = NULL;UINT8       i;BOOLEAN      ret = FALSE;if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL){p_dev = gatt_alloc_bg_dev(bd_addr);}if (p_dev){for (i = 0; i < GATT_MAX_APPS; i ++){if (is_initator){if (p_dev->gatt_if[i] == gatt_if){GATT_TRACE_ERROR("device already in iniator white list");return TRUE;}else if (p_dev->gatt_if[i] == 0){p_dev->gatt_if[i] = gatt_if;if (i == 0)ret = BTM_BleUpdateBgConnDev(TRUE, bd_addr);//update bg connectionelseret = TRUE;break;}}else{/*accept role 的处理*/}}}return ret;
}

我们接着看:

/*******************************************************************************
**
** Function         BTM_BleUpdateBgConnDev
**
** Description      This function is called to add or remove a device into/from
**                  background connection procedure. The background connection
*                   procedure is decided by the background connection type, it can be
*                   auto connection, or selective connection.
**
** Parameters       add_remove: TRUE to add; FALSE to remove.
**                  remote_bda: device address to add/remove.
**
** Returns          void
**
*******************************************************************************/
BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR   remote_bda)
{BTM_TRACE_EVENT("%s() add=%d", __func__, add_remove);return btm_update_dev_to_white_list(add_remove, remote_bda);//add
}

我们继续分析btm_update_dev_to_white_list:

/*******************************************************************************
**
** Function         btm_update_dev_to_white_list
**
** Description      This function adds or removes a device into/from
**                  the white list.
**
*******************************************************************************/
BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr)
{tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;if (to_add && p_cb->white_list_avail_size == 0){BTM_TRACE_ERROR("%s Whitelist full, unable to add device", __func__);return FALSE;}if (to_add)background_connection_add((bt_bdaddr_t*)bd_addr);//addelsebackground_connection_remove((bt_bdaddr_t*)bd_addr);//removebtm_suspend_wl_activity(p_cb->wl_state);//因为当前的WL没有行为,所以do nothingbtm_enq_wl_dev_operation(to_add, bd_addr);//更新btm_cb.ble_ctr_cb.wl_op_qbtm_resume_wl_activity(p_cb->wl_state);//恢复WL的行为return TRUE;
}

上面的逻辑很简单,就是把设备的地址add 到bg connection里面。
然后更新whitelist的列表,然后重新调度WL的行为。

我们先看看background_connection_add的实现:

static void background_connection_add(bt_bdaddr_t *address) {assert(address);background_connections_lazy_init();background_connection_t *connection = hash_map_get(background_connections, address);if (!connection) {connection = osi_calloc(sizeof(background_connection_t));connection->address = *address;hash_map_set(background_connections, &(connection->address), connection);}
}

组建了 key value 键值对放置 到 background connection里面。
我们在看看btm_resume_wl_activity 的实现:

/*******************************************************************************
** Function         btm_resume_wl_activity
** Description      This function is to resume white list related activity
** Returns          none.
*******************************************************************************/
static void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state)
{btm_ble_resume_bg_conn();if (wl_state & BTM_BLE_WL_ADV){btm_ble_start_adv();}}

我们看btm_ble_resume_bg_conn的实现:

/*******************************************************************************
** Function         btm_ble_resume_bg_conn
** Description      This function is to resume a background auto connection procedure.
** Parameters       none.
** Returns          none.
*******************************************************************************/
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);//开始auto connectionif (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;
}

我们继续往下看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;UINT16 scan_int;UINT16 scan_win;UINT8 own_addr_type = p_cb->addr_mgnt_cb.own_addr_type;UINT8 peer_addr_type = BLE_ADDR_PUBLIC;if (start){if ( p_cb->conn_state == BLE_CONN_IDLE ){exec = btm_execute_wl_dev_operation();//btm_add_dev_to_controller-->btsnd_hcic_ble_add_white_list}if (p_cb->conn_state == BLE_CONN_IDLE && background_connections_pending()&& btm_ble_topology_check(BTM_BLE_STATE_INIT)){p_cb->wl_state  |= BTM_BLE_WL_INIT;
...scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF) ?BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int;scan_win = (p_cb->scan_win == BTM_BLE_SCAN_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     */peer_addr_type,        /* UINT8 addr_type_peer */dummy_bda,              /* BD_ADDR bda_peer     */own_addr_type,          /* UINT8 addr_type_own */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;p_cb->wl_state &= ~BTM_BLE_WL_INIT;}else{btm_ble_set_conn_st (BLE_BG_CONN);//set BLE connection state}}else{exec = FALSE;}}...return exec;
}

上面执行的任务主要就是把设备加入到controller 的whitelist里面,然后下发LE connect 命令到controller,
我们简单看下btm_execute_wl_dev_operation:

/*******************************************************************************
**
** 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;//btm_enq_wl_dev_operation 更新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);//下发HCI commandmemset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP));}elsebreak;}return rt;
}

好了,关于BTA_GATTC_Open 就分析到这里,下面我们看看第二点:

2. BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL)的流程:

void BTA_DmBleSetBgConnType(tBTA_DM_BLE_CONN_TYPE bg_conn_type, tBTA_DM_BLE_SEL_CBACK *p_select_cback)
{#if BLE_INCLUDED == TRUEtBTA_DM_API_BLE_SET_BG_CONN_TYPE    *p_msg;if ((p_msg = (tBTA_DM_API_BLE_SET_BG_CONN_TYPE *) GKI_getbuf(sizeof(tBTA_DM_API_BLE_SET_BG_CONN_TYPE))) != NULL){memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_SET_BG_CONN_TYPE));p_msg->hdr.event        = BTA_DM_API_BLE_SET_BG_CONN_TYPE;p_msg->bg_conn_type     = bg_conn_type;p_msg->p_select_cback   = p_select_cback;bta_sys_sendmsg(p_msg);}
#endif
}

执行的函数是

void bta_dm_ble_set_bg_conn_type (tBTA_DM_MSG *p_data)
{BTM_BleSetBgConnType(p_data->ble_set_bd_conn_type.bg_conn_type, p_data->ble_set_bd_conn_type.p_select_cback);
}
BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE   bg_conn_type,tBTM_BLE_SEL_CBACK   *p_select_cback)
{BOOLEAN started = TRUE;BTM_TRACE_EVENT ("BTM_BleSetBgConnType ");if (!controller_get_interface()->supports_ble())return FALSE;if (btm_cb.ble_ctr_cb.bg_conn_type != bg_conn_type){switch (bg_conn_type){case BTM_BLE_CONN_AUTO:btm_ble_start_auto_conn(TRUE);break;

发现这里最终也会执行到btm_ble_start_auto_conn,流程和上面的流程一样,就不赘述。

关于BLE设备回连的问题就先分析到这里。

本文出自:《蓝牙BLE设备主机重启回连流程分析》
整理一些写的还不错的蓝牙文档: https://www.cnblogs.com/libs-liu/category/937790.html

蓝牙BLE设备主机重启回连流程分析相关推荐

  1. 安卓读取蓝牙BLE设备信息

    安卓读取蓝牙BLE设备信息 简介 轮询方式代码实现 监听广播方式代码实现 简介 目前,许多项目都会涉及与BLE设备进行交互的功能,接下来说一下读取BLE设备信息的具体实现流程.安卓BLE相关接口介绍详 ...

  2. Android-低功耗蓝牙(BLE)-客户端(主机/中心设备)和服务端(从机/外围设备)

    参考: https://developer.android.com/guide/topics/connectivity/bluetooth-le http://a1anwang.com/post-47 ...

  3. 蓝牙BLE设备连接与通信

    1.在建立BLE设备进行通信时注意3点 1.1.蓝牙位置权限必须动态申请(Android 6.0以上),如果没有动态申请会出现下面的问题 Need BLUETOOTH permission: Neit ...

  4. Win10 平台C#与低功耗蓝牙BLE设备通信案例

    前几天接了个单,客户要在win10电脑上做个工具软件,跟蓝牙锁设备相互通信.一开始以为是普通的蓝牙设备呢,收到客户寄来的测试设备,才发现是低功耗BLE蓝牙设备. PS:当时我研发用的台式机是没有蓝牙设 ...

  5. Android6.0以上系统搜索不到 蓝牙BLE 设备问题

    最近开发一款软件,一开始一切顺利,但是在连接外围BLE 设备时,发现需要Location的权限, <uses-permission android:name="android.perm ...

  6. 取消蓝牙BLE设备30秒内仅能搜索5次的限制,或者修改相应的需求也行

    override/vendor/mediatek/proprietary/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattServ ...

  7. 蓝牙技术|蓝牙BLE促进物联网资产跟踪的发展

    蓝牙BLE连接相对靠近的设备,同时设备消耗的能量比传统蓝牙低10倍.这是一种节能.低成本的将数据从一系列传感器传输到中央网关的方式.然后网关利用WiFi.手机信号塔或两者将数据发送给决策者,无论他们身 ...

  8. 语言 micropython_MicroPython蓝牙BLE例程实操(一)

    蓝牙作为一种短距通信系统,无疑是十分优秀,也是十分成功的,特别是随着近年物联网的发展,各种智能设备广泛应用,蓝牙BLE以其鲁棒性.低功耗.低成本等特点,逐渐成为各种智能设备的标准配置.如今,借助于ES ...

  9. ESP32:蓝牙BLE控制M3508电机

    ESP32:蓝牙BLE控制M3508电机 先给各位朋友拜个年,祝大家新春快乐,事事顺利,身体健康啊! 还是熟悉的3508,内容概述: ESP32主控 蓝牙BLE通信 使用实时系统(FreeRTOS) ...

最新文章

  1. 熬夜变傻有科学依据,人类睡觉时会被“洗脑”,科学家首次拍下全程
  2. 【每日DP】day14、P2016 战略游戏(树形DP模板)难度⭐⭐⭐
  3. 图解ecshop之批量上传与批量处理
  4. 【原创】项目管理得失经验总结
  5. SMW0上传模板下载到本地
  6. awk按ip统计日志数
  7. linux github代码仓库,centos7上使用git命令把代码放到github上
  8. java肯尼亚_对肯尼亚这个国家,你有什么好奇的?对它的第一印象是什么?
  9. 如何解决api接口的并发问题?
  10. 计算机控制技术期中测试素材2020版
  11. linux制作grub启动u盘启动菜单,用u盘制作grub启动盘[来源不详]
  12. 计算机网络笔记及思维导图(3)——数据链路层
  13. 华为云讲解:2. Istio Pilot 与服务发现
  14. 《深入浅出NodeJS》读书笔记
  15. cannot create temp file for here-document: No space left on device
  16. SpringBoot学习小结之Elasticsearch
  17. 大型API网关(八)—— 超卖和资源隔离
  18. @PostMapping
  19. Linux下IP的配置_F_hawk189_新浪博客
  20. 我的《ANSA快速入门指南》中文帮助文档浅析(上)

热门文章

  1. Python批量监控主机内存/CPU利用率/磁盘/网络等信息
  2. Android应用软件开发如何盈利
  3. 用广度优先搜索探索迷宫问题
  4. pycharm如何查看python文件的工作目录
  5. Y - C~K的班级(III)_Java
  6. uva 1586 - Molar mass
  7. 面向对象设计原则之里氏代换原则
  8. 网站做好了怎样推广?
  9. OKEx公链OKChain 下一个DeFi跑马场?
  10. 如何较快制作温度与电压值对照表