當BLE設備已經完成配對,而且完成GATT服務的搜索,下一步就開始profile 的鏈接流程了,通常LE設備都是走的HOGP的流程,咱們這篇文章就分析一下hogp的鏈接流程。html
鏈接是從framework到JNI,再到協議棧,咱們從JNI 分析流程吧。android
在HID profile中,與framework 層對接的JNI 文件是com_android_bluetooth_hid.cpp,其調用的connct函數是connectHidNative:api
static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) { bt_status_t status; jbyte *addr; jboolean ret = JNI_TRUE; if (!sBluetoothHidInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); if (!addr) { ALOGE("Bluetooth device address null"); return JNI_FALSE; } if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) != BT_STATUS_SUCCESS) {//獲取hid host的interface,而後調用connect的接口 ALOGE("Failed HID channel connection, status: %d", status); ret = JNI_FALSE; } env->ReleaseByteArrayElements(address, addr, 0); return ret; }
咱們看hid的interface:app
static const bthh_interface_t bthhInterface = { sizeof(bthhInterface), init, connect, disconnect, virtual_unplug, set_info, get_protocol, set_protocol, get_report, set_report, send_data, cleanup, }
直接看 connect:less
/******************************************************************************* ** ** Function connect ** ** Description connect to hid device ** ** Returns bt_status_t ** *******************************************************************************/ static bt_status_t connect( bt_bdaddr_t *bd_addr) { if(btif_hh_cb.status != BTIF_HH_DEV_CONNECTING) { btif_transfer_context(btif_hh_handle_evt, BTIF_HH_CONNECT_REQ_EVT, (char*)bd_addr, sizeof(bt_bdaddr_t), NULL);//轉移到btif task中去執行 return BT_STATUS_SUCCESS; } else return BT_STATUS_BUSY; }
/******************************************************************************* ** ** Function btif_hh_handle_evt ** ** Description Switches context for immediate callback ** ** Returns void ** *******************************************************************************/ static void btif_hh_handle_evt(UINT16 event, char *p_param) { bt_bdaddr_t *bd_addr = (bt_bdaddr_t*)p_param; BTIF_TRACE_EVENT("%s: event=%d", __FUNCTION__, event); int ret; switch(event) { case BTIF_HH_CONNECT_REQ_EVT: { ret = btif_hh_connect(bd_addr);//鏈接走到btif if(ret == BT_STATUS_SUCCESS) { HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_CONNECTING);//向上彙報正在鏈接 } else HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_DISCONNECTED); }
看看btif_hh的實現:函數
/******************************************************************************* ** ** Function btif_hh_connect ** ** Description connection initiated from the BTIF thread context ** ** Returns int status ** *******************************************************************************/ bt_status_t btif_hh_connect(bt_bdaddr_t *bd_addr) { btif_hh_device_t *dev; btif_hh_added_device_t *added_dev = NULL; char bda_str[20]; int i; BD_ADDR *bda = (BD_ADDR*)bd_addr; CHECK_BTHH_INIT(); dev = btif_hh_find_dev_by_bda(bd_addr); BTIF_TRACE_DEBUG("Connect _hh"); sprintf(bda_str, "%02X:%02X:%02X:%02X:%02X:%02X", (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]); ... /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways sending this request from host, for subsequent user initiated connection. If the remote is not in pagescan mode, we will do 2 retries to connect before giving up */ tBTA_SEC sec_mask = BTUI_HH_SECURITY; btif_hh_cb.status = BTIF_HH_DEV_CONNECTING; BTA_HhOpen(*bda, BTA_HH_PROTO_RPT_MODE, sec_mask); HAL_CBACK(bt_hh_callbacks, connection_state_cb, bd_addr, BTHH_CONN_STATE_CONNECTING);//這裏再次彙報了鏈接的狀態 return BT_STATUS_SUCCESS; }
這裏留一個問題:此刻調用btif_hh_find_dev_by_bda 有沒有找到相應的記錄?估計是沒有,oop
那麼這裏的重點是 就是BTA_HhOpenle ,從 函數名字看,流程已經走到了BTA 層,繼續看。ui
/******************************************************************************* ** ** Function BTA_HhOpen ** ** Description Connect to a device of specified BD address in specified ** protocol mode and security level. ** ** Returns void ** *******************************************************************************/ void BTA_HhOpen(BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, tBTA_SEC sec_mask) { tBTA_HH_API_CONN *p_buf; p_buf = (tBTA_HH_API_CONN *)GKI_getbuf((UINT16)sizeof(tBTA_HH_API_CONN)); if (p_buf!= NULL) { memset((void *)p_buf, 0, sizeof(tBTA_HH_API_CONN)); p_buf->hdr.event = BTA_HH_API_OPEN_EVT; p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE; p_buf->sec_mask = sec_mask; p_buf->mode = mode; bdcpy(p_buf->bd_addr, dev_bda); bta_sys_sendmsg((void *)p_buf); } else { APPL_TRACE_ERROR("No resource to send HID host Connect request."); } }
想BTU發送了BTA_HH_API_OPEN_EVT 事件,從函數的註釋看,雖然名字上面帶有open,其實就是幹connect的活,咱們繼續看:this
/******************************************************************************* ** ** 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 corresponding CB before proceed */ if (p_msg->event == BTA_HH_API_OPEN_EVT) { index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr); } ... if (index != BTA_HH_IDX_INVALID) p_cb = &bta_hh_cb.kdev[index]; bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg);//進入狀態機 } return (TRUE); }
咱們繼續看狀態機中如何處理:bluedroid中狀態機的處理的流程都i同樣,這裏不作詳細分析:spa
/******************************************************************************* ** ** 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; #if BTA_HH_DEBUG == TRUE tBTA_HH_STATE in_state ; UINT16 debug_event = event; #endif memset(&cback_data, 0, sizeof(tBTA_HH)); ... else { 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; }
看看狀態表:
/* BTA_HH_API_OPEN_EVT */ {BTA_HH_START_SDP, BTA_HH_W4_CONN_ST },
那麼下一個狀態是BTA_HH_W4_CONN_ST,當前執行的action 是BTA_HH_START_SDP ,咱們從action 的名字上面能夠看出,確定是 作HID host相關的搜索的工做,這裏執行的函數是bta_hh_start_sdp:
其實咱們知道,BREDR的設備進行服務搜索走的是sdp的流程,可是BLE 通常走的是GATT的流程。
/******************************************************************************* ** ** Function bta_hh_start_sdp ** ** Description Start SDP service search, and obtain necessary SDP records. ** Only one SDP service search request is allowed at the same ** time. For every BTA_HhOpen API call, do SDP first unless SDP ** has been done previously. ** ** Returns void ** *******************************************************************************/ void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { tBTA_HH_STATUS status = BTA_HH_ERR_SDP; UINT8 hdl; p_cb->sec_mask = p_data->api_conn.sec_mask; p_cb->mode = p_data->api_conn.mode; bta_hh_cb.p_cur = p_cb; #if (BTA_HH_LE_INCLUDED == TRUE) if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)) { bta_hh_le_open_conn(p_cb, p_data->api_conn.bd_addr);//le open return; } #endif ...
這裏判斷是le 的設備,走le 的流程:
/******************************************************************************* ** ** Function bta_hh_le_open_conn ** ** Description open a GATT connection first. ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) { /* update cb_index[] map */ p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); memcpy(p_cb->addr, remote_bda, BD_ADDR_LEN); bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; p_cb->in_use = TRUE; BTA_GATTC_Open(bta_hh_cb.gatt_if, remote_bda, TRUE, BTA_GATT_TRANSPORT_LE);//open }
這裏又走到了BTA_GATTC_Open,咱們在 這篇文章中分析過這個函數,這裏須要注意的是 這裏的bta_hh_cb.gatt_if 是 hogp 註冊到GATT的 interface,這裏打開也是打開 hogp的接口。
從以前的文章分析中得知,在配對以後也會進行BTA_GATTC_Open,只是那時打開的是 device manager註冊到GATT的接口,而且在那個流程已經完成了對於GATT service的查詢。這一次再次打開一個GATT 的接口,應該不會再進行服務的從新搜索,可能會對已經搜索到的服務進行讀取工做。咱們繼續看:
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; p_buf->transport = transport; memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); bta_sys_sendmsg(p_buf); } return; }
發送事件到btu: BTA_GATTC_API_OPEN_EVT;
BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg) { tBTA_GATTC_CB *p_cb = &bta_gattc_cb; tBTA_GATTC_CLCB *p_clcb = NULL; tBTA_GATTC_RCB *p_clreg; BOOLEAN rt = TRUE; switch (p_msg->event) { case BTA_GATTC_API_OPEN_EVT: bta_gattc_process_api_open(p_cb, (tBTA_GATTC_DATA *) p_msg); break; ...
繼續看:
/******************************************************************************* ** ** 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) { if ((p_clcb = bta_gattc_find_alloc_clcb(p_msg->api_conn.client_if, p_msg->api_conn.remote_bda, p_msg->api_conn.transport)) != NULL)//分配一個p_clcb { bta_gattc_sm_execute(p_clcb, event, p_msg); } ... }
進入狀態機,繼續看:,狀態處理的流程也和以前同樣:
/******************************************************************************* ** ** Function bta_gattc_sm_execute ** ** Description State machine event handling function for GATTC ** ** ** Returns BOOLEAN : TRUE if queued client request buffer can be immediately released ** else FALSE ** *******************************************************************************/ BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data) { tBTA_GATTC_ST_TBL state_table; UINT8 action; int i; BOOLEAN rt = TRUE; /* look up the state table for the current state */ state_table = bta_gattc_st_tbl[p_clcb->state]; event &= 0x00FF; /* set next state */ p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE]; /* execute action functions */ for (i = 0; i < BTA_GATTC_ACTIONS; i++) { if ((action = state_table[event][i]) != BTA_GATTC_IGNORE) { (*bta_gattc_action[action])(p_clcb, p_data); if (p_clcb->p_q_cmd == p_data) { /* buffer is queued, don't free in the bta dispatcher. * we free it ourselves when a completion event is received. */ rt = FALSE; } } else { break; } } return rt; }
剛開始的是idle 狀態:
/* BTA_GATTC_API_OPEN_EVT */ {BTA_GATTC_OPEN, BTA_GATTC_W4_CONN_ST},
這裏的狀態是GATT client 的狀態轉換,文章開頭也涉及到狀態機,那個是BTA_HH的狀態轉換,二者不能搞錯,這種狀況應該是屬於狀態機嵌套。
咱們繼續分析GATT client 的狀態轉換,下一個狀態是BTA_GATTC_W4_CONN_ST,執行的函數丨BTA_GATTC_OPEN-->
/******************************************************************************* ** ** Function bta_gattc_open ** ** Description Process API connection function. ** ** Returns void ** *******************************************************************************/ void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { tBTA_GATTC_DATA gattc_data; /* open/hold a connection */ if (!GATT_Connect(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda, TRUE, p_data->api_conn.transport)) { ...//錯誤處理 } else { /* a connected remote device */ if (GATT_GetConnIdIfConnected(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda, &p_clcb->bta_conn_id, p_data->api_conn.transport)) { gattc_data.int_conn.hdr.layer_specific = p_clcb->bta_conn_id; bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data);//仍是在狀態機裏面輪轉 } /* else wait for the callback event */ } }
由於這個link 已經存在了,因此GATT_Connect 確定是 直接返回了。那就是走到狀態機發送新的事件:BTA_GATTC_INT_CONN_EVT,那咱們繼續看這個event 的處理:
/* BTA_GATTC_INT_CONN_EVT */ {BTA_GATTC_CONN, BTA_GATTC_CONN_ST},
下一個狀態是 BTA_GATTC_CONN_ST,執行的action 是BTA_GATTC_CONN,咱們繼續看,
/******************************************************************************* ** ** Function bta_gattc_conn ** ** Description receive connection callback from stack ** ** Returns void ** *******************************************************************************/ void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { tBTA_GATTC_IF gatt_if; if (p_data != NULL) { p_clcb->bta_conn_id = p_data->int_conn.hdr.layer_specific; GATT_GetConnectionInfor(p_data->hdr.layer_specific, &gatt_if, p_clcb->bda, &p_clcb->transport); } p_clcb->p_srcb->connected = TRUE; if (p_clcb->p_srcb->mtu == 0) p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE; /* start database cache if needed */ if (p_clcb->p_srcb->p_srvc_cache == NULL || p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE)//第一次打開的時候會進行service的搜索,如今p_srvc_cache!=NULL,而且p_srcb->state == BTA_GATTC_SERV_IDLE { APPL_TRACE_DEBUG("bta_gattc_conn start database cache if needed libs_liu"); if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) { p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD; bta_gattc_sm_execute(p_clcb, BTA_GATTC_START_CACHE_EVT, NULL); } else /* cache is building */ p_clcb->state = BTA_GATTC_DISCOVER_ST; } else { /* a pending service handle change indication */ if (p_clcb->p_srcb->srvc_hdl_chg) { p_clcb->p_srcb->srvc_hdl_chg = FALSE; /* start discovery */ bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); } } if (p_clcb->p_rcb) { ... bta_gattc_send_open_cback(p_clcb->p_rcb, BTA_GATT_OK, p_clcb->bda, p_clcb->bta_conn_id, p_clcb->transport, p_clcb->p_srcb->mtu);直接上報狀態 } }
這裏注意的是 。由於以前已經打開過了,已經走了gatt service的discovery的動做了,因此不會進行再次discovery了,直接來到上報狀態的流程:
/******************************************************************************* ** ** Function bta_gattc_send_open_cback ** ** Description send open callback ** ** Returns ** *******************************************************************************/ void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status, BD_ADDR remote_bda, UINT16 conn_id, tBTA_TRANSPORT transport, UINT16 mtu) { tBTA_GATTC cb_data; if (p_clreg->p_cback) { memset(&cb_data, 0, sizeof(tBTA_GATTC)); cb_data.open.status = status; cb_data.open.client_if = p_clreg->client_if; cb_data.open.conn_id = conn_id; cb_data.open.mtu = mtu; cb_data.open.transport = transport; bdcpy(cb_data.open.remote_bda, remote_bda); (*p_clreg->p_cback)(BTA_GATTC_OPEN_EVT, &cb_data);//這裏是註冊的回調 } }
這個函數很簡單,就是上報 BTA_GATTC_OPEN_EVT的狀態,這裏有時候會忘記這個回調是在在哪裏註冊的,其實很簡單,看看上報的event 的名字是BTA_GATTC 打頭的,那確定是BTA_GATTC_AppRegister中註冊的。
那註冊的回調函數是什麼呢?註冊的時候是在bta_hh_le_enable,註冊是」BTA HH OVER LE「,
/******************************************************************************* ** ** Function bta_hh_le_enable ** ** Description initialize LE HID related functionality ** ** ** Returns void ** *******************************************************************************/ void bta_hh_le_enable(void) { char app_name[LEN_UUID_128 + 1]; tBT_UUID app_uuid = {LEN_UUID_128,{0}}; UINT8 xx; bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) bta_hh_cb.le_cb_index[xx] = BTA_HH_IDX_INVALID; memset (app_name, 0, LEN_UUID_128 + 1); strncpy(app_name, "BTA HH OVER LE", LEN_UUID_128); memcpy((void *)app_uuid.uu.uuid128, (void *)app_name, LEN_UUID_128); BTA_GATTC_AppRegister(&app_uuid, bta_hh_gattc_callback); return; }
那咱們知道,其實這個回調就是bta_hh_gattc_callback,它是」GATT client callback function used in BTA HH「,咱們看看其處理的event有:
BTA_GATTC_REG_EVT、BTA_GATTC_DEREG_EVT、BTA_GATTC_OPEN_EVT、BTA_GATTC_READ_CHAR_EVT、BTA_GATTC_WRITE_DESCR_EVT、BTA_GATTC_WRITE_CHAR_EVT、BTA_GATTC_CLOSE_EVT、BTA_GATTC_SEARCH_CMPL_EVT、BTA_GATTC_SEARCH_RES_EVT、BTA_GATTC_NOTIF_EVT、BTA_GATTC_ENC_CMPL_CB_EVT、BTA_GATTC_CFG_MTU_EVT
從上面處理的事件來看,其回調幾乎處理了hogp 相關的全部的事件,包括讀寫各類屬性,搜索完成,以及搜索結果,以及LE設備的消息通知,都是從這裏來處理,可見其是GATT流程中的重要一環。
咱們繼續分析BTA_GATTC_OPEN_EVT 的處理:
/******************************************************************************* ** ** Function bta_hh_gattc_callback ** ** Description This is GATT client callback function used in BTA HH. ** ** Parameters: ** *******************************************************************************/ static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) { tBTA_HH_DEV_CB *p_dev_cb; UINT16 evt; if (p_data == NULL) return; switch (event) { ... case BTA_GATTC_OPEN_EVT: /* 2 */ p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->open.remote_bda); if (p_dev_cb) { bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_OPEN_EVT, (tBTA_HH_DATA *)&p_data->open);//這裏咱們發現跳到HH相關的狀態機 } break;
這個 事件是關聯模塊是BTA_ID_HH ,這裏又轉回了文章開始的時候的狀態機
#define BTA_ID_HH 23 /* Human Interface Device Host */
看看 bta_hh_sm_execute的處理,當前的狀態是BTA_HH_W4_CONN_ST ,
/* BTA_HH_GATT_OPEN_EVT */ ,{BTA_HH_GATT_OPEN, BTA_HH_W4_CONN_ST }
下一個狀態仍是BTA_HH_W4_CONN_ST ,執行的action 是BTA_HH_GATT_OPEN,咱們看看其實現:
/******************************************************************************* ** ** Function bta_hh_gatt_open ** ** Description process GATT open event. ** ** Parameters: ** *******************************************************************************/ void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) { tBTA_GATTC_OPEN *p_data = &p_buf->le_open; UINT8 *p2; tHID_STATUS status = BTA_HH_ERR; /* if received invalid callback data , ignore it */ if (p_cb == NULL || p_data == NULL) return; p2 = p_data->remote_bda; if (p_data->status == BTA_GATT_OK) { p_cb->is_le_device = TRUE; p_cb->in_use = TRUE; p_cb->conn_id = p_data->conn_id; 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; bta_hh_sm_execute(p_cb, BTA_HH_START_ENC_EVT, NULL);//開始進行encryption相關動做 } else /* open failure */ { bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); } }
咱們繼續看:
/* BTA_HH_START_ENC_EVT */ ,{BTA_HH_START_SEC, BTA_HH_W4_SEC }
下一個狀態是 BTA_HH_W4_SEC,執行的操做是BTA_HH_START_SEC,咱們繼續看:
/******************************************************************************* ** ** Function bta_hh_start_security ** ** Description start the security check of the established connection ** ** Parameters: ** *******************************************************************************/ void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) { UINT8 sec_flag=0; tBTM_SEC_DEV_REC *p_dev_rec; UNUSED(p_buf); p_dev_rec = btm_find_dev(p_cb->addr); ... /* verify bond */ BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE);//獲取設備的sec flag /* if link has been encrypted */ if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL);//繼續狀態機 } /* if bonded and link not encrypted */ else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) { ... /* otherwise let it go through */ else { bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); } }
咱們繼續分析狀態機流程:
/* BTA_HH_ENC_CMPL_EVT */ {BTA_HH_SEC_CMPL, BTA_HH_W4_CONN_ST },
新狀態變成了BTA_HH_W4_CONN_ST,執行的action是BTA_HH_SEC_CMPL,咱們看看bta_hh_security_cmpl 的具體實現:
/******************************************************************************* ** ** Function bta_hh_security_cmpl ** ** Description Security check completed, start the service discovery ** if no cache available, otherwise report connection open completed ** ** Parameters: ** *******************************************************************************/ void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) { tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache; UINT8 num_rpt = 0; UNUSED(p_buf); if (p_cb->status == BTA_HH_OK) { if (!p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) { /* start loading the cache if not in stack */ if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt, p_cb->app_id)) != NULL) { bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt); } } /* discovery has been done for HID service */ if (p_cb->app_id != 0 && p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) { /* configure protocol mode */ if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == FALSE) { APPL_TRACE_ERROR("bta_hh_security_cmpl"); bta_hh_le_open_cmpl(p_cb); } } /* start primary service discovery for HID service */ else { bta_hh_le_pri_service_discovery(p_cb);//繼續進行hid service的discovery } } else { ... } }
下面進行的hid service 的搜索,也就是 hogp的範疇了:bta_hh_le_pri_service_discovery:
/******************************************************************************* ** ** Function bta_hh_le_pri_service_discovery ** ** Description Initialize GATT discovery on the remote LE HID device by opening ** a GATT connection first. ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB *p_cb) { tBT_UUID pri_srvc; bta_hh_le_co_reset_rpt_cache(p_cb->addr, p_cb->app_id); p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS|BTA_HH_LE_DISC_DIS);//這裏是標誌位,標誌要搜索兩個服務,當兩個服務搜索完成,會調用bta_hh_le_open_cmpl 來上報狀態 /* read DIS info */ if (!DIS_ReadDISInfo(p_cb->addr, bta_hh_le_dis_cback, DIS_ATTR_PNP_ID_BIT))//注意第三個參數,讀的是PID、VID { APPL_TRACE_ERROR("read DIS failed"); p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; } /* in parallel */ /* start primary service discovery for HID service */ pri_srvc.len = LEN_UUID_16; pri_srvc.uu.uuid16 = UUID_SERVCLASS_LE_HID; BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc);//開始HID 的service的搜索 return; }
這裏分爲兩個部分:
/******************************************************************************* ** ** Function DIS_ReadDISInfo ** ** Description Read remote device DIS information. ** ** Returns void ** *******************************************************************************/ BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback, tDIS_ATTR_MASK mask) { UINT16 conn_id; /* Initialize the DIS client if it hasn't been initialized already. */ srvc_eng_init();//對DIS client 進行初始化,並註冊了srvc_eng_cb.gatt_if 到GATT ... dis_cb.p_read_dis_cback = p_cback;//保存callback = bta_hh_le_dis_cback /* Mark currently active operation */ dis_cb.dis_read_uuid_idx = 0; dis_cb.request_mask = mask; GATT_GetConnIdIfConnected(srvc_eng_cb.gatt_if, peer_bda, &conn_id, BT_TRANSPORT_LE); /* need to enhance it as multiple service is needed */ srvc_eng_request_channel(peer_bda, SRVC_ID_DIS); ... return dis_gatt_c_read_dis_req(conn_id);//讀PID VID }
咱們繼續看:
/******************************************************************************* ** ** Function dis_gatt_c_read_dis_req ** ** Description Read remote device DIS attribute request. ** ** Returns void ** *******************************************************************************/ BOOLEAN dis_gatt_c_read_dis_req(UINT16 conn_id) { tGATT_READ_PARAM param; memset(¶m, 0, sizeof(tGATT_READ_PARAM)); param.service.uuid.len = LEN_UUID_16; param.service.s_handle = 1; param.service.e_handle = 0xFFFF; param.service.auth_req = 0; while (dis_cb.dis_read_uuid_idx < DIS_MAX_CHAR_NUM) { if (dis_uuid_to_attr(dis_attr_uuid[dis_cb.dis_read_uuid_idx]) & dis_cb.request_mask)//根據mask 來讀相關的屬性 { param.service.uuid.uu.uuid16 = dis_attr_uuid[dis_cb.dis_read_uuid_idx]; if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) == GATT_SUCCESS)//開始讀 return TRUE; GATT_TRACE_ERROR ("Read DISInfo: 0x%04x GATT_Read Failed", param.service.uuid.uu.uuid16); } dis_cb.dis_read_uuid_idx++; } dis_gatt_c_read_dis_value_cmpl(conn_id); return(FALSE); }
讀寫成功就直接返回true。
這裏簡單看一下,對於DIS 結果的處理:
gatt_process_read_by_type_rsp -->(p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE)-->gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p);--->(*p_cmpl_cb)(conn_id, op, status, &cb_data);(這裏須要注意,由於註冊這個eng的時候,沒有註冊discovery result或者discovery complete等接口,因此調用的是p_cmpl_cb,實際調用的函數就是srvc_eng_c_cmpl_cback),--->srvc_eng_c_cmpl_cback-->srvc_eng_c_cmpl_act[0]--->dis_c_cmpl_cback:
/******************************************************************************* ** ** Function dis_c_cmpl_cback ** ** Description Client operation complete callback. ** ** Returns void ** *******************************************************************************/ void dis_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) { UINT16 read_type = dis_attr_uuid[dis_cb.dis_read_uuid_idx]; UINT8 *pp = NULL, *p_str; UINT16 conn_id = p_clcb->conn_id; ... case GATT_UUID_PNP_ID: if (p_data->att_value.len == DIS_PNP_ID_SIZE)//保存pid vid { p_clcb->dis_value.attr_mask |= DIS_ATTR_PNP_ID_BIT; STREAM_TO_UINT8 (p_clcb->dis_value.pnp_id.vendor_id_src, pp); STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.vendor_id, pp); STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_id, pp); STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_version, pp); } break; ... dis_cb.dis_read_uuid_idx ++; dis_gatt_c_read_dis_req(conn_id);//繼續搜索
繼續搜索的結果是 根據mask來斷定的。若是當初mask只設定一個值,那麼就會直接調用discovery 完成事件:
/******************************************************************************* ** ** Function dis_gatt_c_read_dis_value_cmpl ** ** Description Client read DIS database complete callback. ** ** Returns void ** *******************************************************************************/ static void dis_gatt_c_read_dis_value_cmpl(UINT16 conn_id) { tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id); dis_cb.dis_read_uuid_idx = 0xff; srvc_eng_release_channel(conn_id); if (dis_cb.p_read_dis_cback && p_clcb)//調用進行搜索的時候(DIS_ReadDISInfo)註冊的函數bta_hh_le_dis_cback { LOG_INFO("%s conn_id:%d attr_mask = 0x%04x", __func__, conn_id, p_clcb->dis_value.attr_mask); (*dis_cb.p_read_dis_cback)(p_clcb->bda, &p_clcb->dis_value); dis_cb.p_read_dis_cback = NULL; } }
下面咱們 看看 第二點:
/******************************************************************************* ** ** Function BTA_GATTC_ServiceSearchRequest ** ** Description This function is called to request a GATT service discovery ** on a GATT server. This function report service search result ** by a callback event, and followed by a service search complete ** event. ** ** Parameters conn_id: connection ID. ** p_srvc_uuid: a UUID of the service application is interested in. ** If Null, discover for all services. ** ** Returns None ** *******************************************************************************/ void BTA_GATTC_ServiceSearchRequest (UINT16 conn_id, tBT_UUID *p_srvc_uuid) { tBTA_GATTC_API_SEARCH *p_buf; UINT16 len = sizeof(tBTA_GATTC_API_SEARCH) + sizeof(tBT_UUID); if ((p_buf = (tBTA_GATTC_API_SEARCH *) GKI_getbuf(len)) != NULL) { memset(p_buf, 0, len); p_buf->hdr.event = BTA_GATTC_API_SEARCH_EVT;//gatt search p_buf->hdr.layer_specific = conn_id; if (p_srvc_uuid) { p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1); memcpy(p_buf->p_srvc_uuid, p_srvc_uuid, sizeof(tBT_UUID)); } else p_buf->p_srvc_uuid = NULL; bta_sys_sendmsg(p_buf); } return; }
咱們繼續看,上面的流程就是觸發」GATT service discovery「 ,那這裏又進入了熟悉的狀態機輪轉,當前的狀態機的狀態是已經鏈接的狀態:
BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data) { tBTA_GATTC_ST_TBL state_table; UINT8 action; int i; BOOLEAN rt = TRUE; /* look up the state table for the current state */ state_table = bta_gattc_st_tbl[p_clcb->state]; event &= 0x00FF; /* set next state */ p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE]; /* execute action functions */ for (i = 0; i < BTA_GATTC_ACTIONS; i++) { if ((action = state_table[event][i]) != BTA_GATTC_IGNORE) { (*bta_gattc_action[action])(p_clcb, p_data); if (p_clcb->p_q_cmd == p_data) { /* buffer is queued, don't free in the bta dispatcher. * we free it ourselves when a completion event is received. */ rt = FALSE; } } else { break; } } return rt; }
/* BTA_GATTC_API_SEARCH_EVT */ {BTA_GATTC_SEARCH, BTA_GATTC_CONN_ST},
下一個狀態 仍是BTA_GATTC_CONN_ST ,執行的action 是BTA_GATTC_SEARCH:
/******************************************************************************* ** ** Function bta_gattc_search ** ** Description start a search in the local server cache ** ** Returns None. ** *******************************************************************************/ void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR; tBTA_GATTC cb_data; APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d",p_clcb->bta_conn_id);//注意這裏的p_clcb是hogp 註冊到GATT的接口 if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) { status = BTA_GATT_OK; /* search the local cache of a server device */ bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);//這裏的uuid = UUID_SERVCLASS_LE_HID 0x1812 } cb_data.search_cmpl.status = status; cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id; /* end of search or no server cache available */ ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT, &cb_data); }//bta_hh_gattc_callback
search 這個接口,他實現的思想是先 搜索cache 裏面有沒有相關的服務,若是cache 存在,那麼就會 調用回調(再GATT register註冊的)進行上報,這裏 是已經了cache,直接load,load完成以後還會經過BTA_GATTC_SEARCH_RES_EVT把結果(p_dev_cb->hid_srvc[idx].in_use = TRUE)保存在p_dev_cb。 咱們直接看看 下面的回調函數的處理:
/******************************************************************************* ** ** Function bta_hh_gattc_callback ** ** Description This is GATT client callback function used in BTA HH. ** ** Parameters: ** *******************************************************************************/ static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) { tBTA_HH_DEV_CB *p_dev_cb; UINT16 evt; ... case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */ bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl); break; ...
繼續看bta_hh_le_srvc_search_cmpl:
/******************************************************************************* ** ** Function bta_hh_le_srvc_search_cmpl ** ** Description This function process the GATT service search complete. ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data) { tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); ... if(p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0) { ... } /* GATT service discovery sucessfully finished */ else { if (p_dev_cb->disc_active & BTA_HH_LE_DISC_SCPS)//active = 1+2 = DIS+HID { p_dev_cb->disc_active &= ~BTA_HH_LE_DISC_SCPS; bta_hh_le_open_cmpl(p_dev_cb); } else /* discover HID service */ { p_dev_cb->cur_srvc_index = 0; bta_hh_le_srvc_expl_srvc(p_dev_cb); } } }
如今開始 真正的HID services 的搜索,這裏由於已經load 了相關的LE 的pri service,因此接下來的搜索應該是直接對handle 進行搜索,咱們繼續看bta_hh_le_srvc_expl_srvc的實現:
/******************************************************************************* ** ** Function bta_hh_le_srvc_expl_srvc ** ** Description This function discover the next avaible HID service. ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb) { if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX && p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use) { if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc) /* explore included service first */ bta_hh_le_search_hid_included(p_dev_cb);//先進行include的搜索 else { /* explore characterisc */ p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; bta_hh_le_search_hid_chars(p_dev_cb); } } else /* all service discvery finished */ { bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status); } }
最早進行的是include的服務的搜索,而後應該是 chars,這裏發現,這個include 的搜索 就是搜索cache 裏面是否是有相應的項目,分析代碼發現,搜索的是電池服務UUID_SERVCLASS_BATTERY, 無論有沒有搜索到,都會設置p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc 爲true,而後繼續調用bta_hh_le_srvc_expl_srvc進行搜索,那麼接下來就是explore characterisc 的流程了bta_hh_le_search_hid_chars:
/******************************************************************************* ** ** Function bta_hh_le_search_hid_chars ** ** Description This function discover all characteristics a service and ** all descriptors available. ** ** Parameters: ** *******************************************************************************/ static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb) { tBT_UUID char_cond; tBTA_GATTC_CHAR_ID char_result; tBTA_GATT_CHAR_PROP prop; BOOLEAN next = TRUE; UINT16 char_uuid = 0; tBTA_GATT_SRVC_ID srvc_id; if (p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx == BTA_HH_LE_DISC_CHAR_NUM || (p_dev_cb->status != BTA_HH_OK && p_dev_cb->status != BTA_HH_ERR_PROTO)) { p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; /* explore next service */ p_dev_cb->cur_srvc_index ++; bta_hh_le_srvc_expl_srvc(p_dev_cb);//搜索完成,繼續調用此函數,進入到 return; } p_dev_cb->hid_srvc[ p_dev_cb->cur_srvc_index].cur_expl_char_idx ++; char_uuid = bta_hh_le_disc_char_uuid[p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx - 1];//總共有8個chars 等待搜索 char_cond.len = LEN_UUID_16; char_cond.uu.uuid16 = char_uuid; bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, &srvc_id, &char_cond, &char_result, &prop) == BTA_GATT_OK)//查找 相應的UUID { switch (char_uuid) { case GATT_UUID_HID_CONTROL_POINT: p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_CP_BIT; next = TRUE; break; case GATT_UUID_HID_INFORMATION: case GATT_UUID_HID_REPORT_MAP: /* read the char value */ BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, &char_result, BTA_GATT_AUTH_REQ_NONE);//對於相應的uuid 進行搜索,而且涉及到uuid2handle的轉換 next = FALSE; break; case GATT_UUID_HID_PROTO_MODE: p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_PROTO_MODE_BIT; next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); break; case GATT_UUID_HID_REPORT: #ifdef BLUETOOTH_RTK_BQB if(cert_conf_mask&BLUETOOTH_RTK_BQB_PATCH_HOGP) { APPL_TRACE_ERROR("BQB:this is seen in hogp bqb test"); BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, &char_result, BTA_GATT_AUTH_REQ_NONE); memcpy(&(p_dev_cb->bqb_report_char_result), &char_result, sizeof(tBTA_GATTC_CHAR_ID)); memcpy(&(p_dev_cb->bqb_report_char_cond), &char_cond, sizeof(tBT_UUID)); p_dev_cb->bqb_report_prop = prop; } else #endif bta_hh_le_expl_rpt(p_dev_cb, &char_result, &char_cond, prop); next = FALSE; break; /* found boot mode report types */ case GATT_UUID_HID_BT_KB_OUTPUT: case GATT_UUID_HID_BT_MOUSE_INPUT: case GATT_UUID_HID_BT_KB_INPUT: bta_hh_le_expl_boot_rpt(p_dev_cb, char_uuid, prop); break; } } else { if (char_uuid == GATT_UUID_HID_PROTO_MODE) next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); } if (next == TRUE) { bta_hh_le_search_hid_chars(p_dev_cb); } }
從上面咱們發現對於不一樣的UUID,每每有不一樣的函數去處理,當全部的char 都被搜索完成了之後,就又會執行bta_hh_le_srvc_expl_srvc 來結束這個流程。
這裏先看下 咱們上面搜索的有哪些UUID:
static const UINT16 bta_hh_le_disc_char_uuid[BTA_HH_LE_DISC_CHAR_NUM] = { GATT_UUID_HID_INFORMATION, GATT_UUID_HID_REPORT_MAP, GATT_UUID_HID_CONTROL_POINT, GATT_UUID_HID_REPORT, GATT_UUID_HID_BT_KB_INPUT, GATT_UUID_HID_BT_KB_OUTPUT, GATT_UUID_HID_BT_MOUSE_INPUT, GATT_UUID_HID_PROTO_MODE /* always make sure this is the last attribute to discover */ }
咱們再次看一下bta_hh_le_srvc_expl_srvc:
void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb) { #if BTA_HH_DEBUG == TRUE APPL_TRACE_DEBUG("bta_hh_le_srvc_expl_srvc cur_srvc_index = %d in_use = %d", p_dev_cb->cur_srvc_index, p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use); #endif if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX && p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use)// { if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc) /* explore included service first */ bta_hh_le_search_hid_included(p_dev_cb); else { /* explore characterisc */ p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; bta_hh_le_search_hid_chars(p_dev_cb); } } else /* all service discvery finished */ { bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status);//全部的搜索已經完成 } }
咱們繼續看bta_hh_le_gatt_disc_cmpl:
/******************************************************************************* ** ** Function bta_hh_le_gatt_disc_cmpl ** ** Description Check to see if the remote device is a LE only device ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_gatt_disc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_STATUS status) { /* if open sucessful or protocol mode not desired, keep the connection open but inform app */ if (status == BTA_HH_OK || status == BTA_HH_ERR_PROTO) { /* assign a special APP ID temp, since device type unknown */ p_cb->app_id = BTA_HH_APP_ID_LE; /* set report notification configuration */ p_cb->clt_cfg_idx = 0; bta_hh_le_write_rpt_clt_cfg(p_cb, BTA_HH_LE_SRVC_DEF);//開始作report的配置工做 } else /* error, close the GATT connection */ { /* close GATT connection if it's on */ bta_hh_le_api_disc_act(p_cb); } }
上面的配置的工做就是把全部的input 的屬性,都配置成notification 的形式。
這裏關於hogp的服務搜索基本完成,下面 再說一下 每次配置屬性以後進行的回調的流程:
每次一個行爲配置完成都會調用:bta_gattc_op_cmpl:
/******************************************************************************* ** ** Function bta_gattc_op_cmpl ** ** Description operation completed. ** ** Returns None. ** *******************************************************************************/ void bta_gattc_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { UINT8 op = (UINT8)p_data->op_cmpl.op_code; UINT8 mapped_op = 0; ... else if (op == GATTC_OPTYPE_WRITE) bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl); ...
咱們看看bta_gattc_write_cmpl的實現:
.... event = BTA_GATTC_WRITE_DESCR_EVT; utl_freebuf((void **)&p_clcb->p_q_cmd); cb_data.write.conn_id = p_clcb->bta_conn_id; /* write complete, callback */ ( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data); ...
調用註冊的時候的回調:bta_hh_gattc_callback:
... case BTA_GATTC_WRITE_DESCR_EVT: /* 9 */ case BTA_GATTC_WRITE_CHAR_EVT: /* 4 */ p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->write.conn_id); if (event == BTA_GATTC_WRITE_CHAR_EVT) evt = BTA_HH_GATT_WRITE_CHAR_CMPL_EVT; else evt = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT; bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->write); break;
這裏注意,此時的bta_hh的狀態應是BTA_HH_W4_CONN_ST ,咱們看其狀態機的轉換:event = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT
/* WRITE_DESCR_CMPL_EVT */ ,{BTA_HH_WRITE_DESCR, BTA_HH_W4_CONN_ST }
下一個狀態不變,執行的函數是:bta_hh_le_write_char_descr_cmpl:該函數的實現套路仍是同樣的,就是繼續以前的行爲,若是是已經搜索完成,那麼會有回調向上:
/******************************************************************************* ** ** Function bta_hh_le_write_char_descr_cmpl ** ** Description Write charactersitic descriptor complete event ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) { tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; UINT8 srvc_inst_id, hid_inst_id; /* only write client configuration possible */ if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { srvc_inst_id = p_data->srvc_id.id.inst_id; hid_inst_id = srvc_inst_id; switch (p_data->char_id.uuid.uu.uuid16) { case GATT_UUID_BATTERY_LEVEL: /* battery level clt cfg registered */ hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_dev_cb, srvc_inst_id); /* fall through */ case GATT_UUID_HID_BT_KB_INPUT: case GATT_UUID_HID_BT_MOUSE_INPUT: case GATT_UUID_HID_REPORT: if (p_data->status == BTA_GATT_OK) p_dev_cb->hid_srvc[hid_inst_id].report[p_dev_cb->clt_cfg_idx].client_cfg_value = BTA_GATT_CLT_CONFIG_NOTIFICATION; p_dev_cb->clt_cfg_idx ++; bta_hh_le_write_rpt_clt_cfg(p_dev_cb, hid_inst_id);//繼續write break;
咱們繼續分析:在bta_hh_le_write_rpt_clt_cfg 這個函數裏面 ,確定要發生」質變「:
/******************************************************************************* ** ** Function bta_hh_le_write_rpt_clt_cfg ** ** Description write client configuration. This is only for input report ** enable all input notification upon connection open. ** *******************************************************************************/ BOOLEAN bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst_id) { UINT8 i; tBTA_HH_LE_RPT *p_rpt = &p_cb->hid_srvc[srvc_inst_id].report[p_cb->clt_cfg_idx]; UINT16 srvc_uuid; for (i = p_cb->clt_cfg_idx; i < BTA_HH_LE_RPT_MAX && p_rpt->in_use; i ++, p_rpt ++) { /* enable notification for all input report, regardless mode */ if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) srvc_uuid = UUID_SERVCLASS_BATTERY; else srvc_uuid = UUID_SERVCLASS_LE_HID; if (bta_hh_le_write_char_clt_cfg(p_cb, BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), srvc_uuid, BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), p_rpt->uuid, BTA_GATT_CLT_CONFIG_NOTIFICATION)) { p_cb->clt_cfg_idx = i; return TRUE; } } } p_cb->clt_cfg_idx = 0;//執行到這裏說明,write 行爲已經所有結束 /* client configuration is completed, send open callback */ if (p_cb->state == BTA_HH_W4_CONN_ST)//此時bta_hh 正是BTA_HH_W4_CONN_ST { p_cb->disc_active &= ~BTA_HH_LE_DISC_HIDS;//清掉 這個標誌,也意味着hid service流程完成 /* discover scan parameter profile is act as report host */ bta_hh_le_search_scps(p_cb);//進行scan parameter service 流程 } return FALSE; }
上面 的函數已經註釋,咱們繼續看bta_hh_le_search_scps:
/******************************************************************************* ** ** Function bta_hh_le_search_scps ** ** Description discovery scan parameter service if act as report host, otherwise ** finish LE connection. ** ** Parameters: ** *******************************************************************************/ static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb) { tBT_UUID pri_srvc; if ( p_cb->mode == BTA_HH_PROTO_RPT_MODE)//猜想:搜索完成會設置這個標誌位 { p_cb->disc_active |= BTA_HH_LE_DISC_SCPS;//設置此標誌位 /* start service discovery for Scan Parameter service */ pri_srvc.len = LEN_UUID_16; pri_srvc.uu.uuid16 = UUID_SERVCLASS_SCAN_PARAM; BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); }//開始搜索 else bta_hh_le_open_cmpl(p_cb); }
繼續看:會發出0x1f09 繼續搜索:
void BTA_GATTC_ServiceSearchRequest (UINT16 conn_id, tBT_UUID *p_srvc_uuid) { tBTA_GATTC_API_SEARCH *p_buf; UINT16 len = sizeof(tBTA_GATTC_API_SEARCH) + sizeof(tBT_UUID); if ((p_buf = (tBTA_GATTC_API_SEARCH *) GKI_getbuf(len)) != NULL) { memset(p_buf, 0, len); p_buf->hdr.event = BTA_GATTC_API_SEARCH_EVT; p_buf->hdr.layer_specific = conn_id; if (p_srvc_uuid) { p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1); memcpy(p_buf->p_srvc_uuid, p_srvc_uuid, sizeof(tBT_UUID)); } else p_buf->p_srvc_uuid = NULL; bta_sys_sendmsg(p_buf); } return; }
繼續執行bta_gattc_search:
void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR; tBTA_GATTC cb_data; APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d",p_clcb->bta_conn_id); if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) { status = BTA_GATT_OK; /* search the local cache of a server device */ bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);//seach local cache } cb_data.search_cmpl.status = status; cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id; /* end of search or no server cache available */ ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT, &cb_data);//無論結果,直接上報BTA_GATTC_SEARCH_CMPL_EVT }
這裏會對cache 裏面去搜索,這個看看btsnoop 發現其是能夠搜索到的:經過btsnoop,咱們發現從handle 74-79 就是scan parameter的相關屬性。
下面咱們看看回調函數 對於BTA_GATTC_SEARCH_CMPL_EVT的處理:
static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) { tBTA_HH_DEV_CB *p_dev_cb; UINT16 evt; ... case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */ bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl); break; ...
繼續看:
/******************************************************************************* ** ** Function bta_hh_le_srvc_search_cmpl ** ** Description This function process the GATT service search complete. ** ** Parameters: ** *******************************************************************************/ void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data) { tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); if(p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0) { ... } /* GATT service discovery sucessfully finished */ else { if (p_dev_cb->disc_active & BTA_HH_LE_DISC_SCPS) { p_dev_cb->disc_active &= ~BTA_HH_LE_DISC_SCPS;//清掉scps標誌 bta_hh_le_open_cmpl(p_dev_cb);//上報hh open event } else /* discover HID service */ { p_dev_cb->cur_srvc_index = 0; bta_hh_le_srvc_expl_srvc(p_dev_cb); } } }
調用bta_hh_le_open_cmpl 上報狀態,彙報」HID over GATT connection sucessfully opened「,咱們繼續分析:
/******************************************************************************* ** ** Function bta_hh_le_open_cmpl ** ** Description HID over GATT connection sucessfully opened ** *******************************************************************************/ void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb) { if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE) { bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE);//註冊input notification bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL);//狀態機繼續輪轉 #if (BTA_HH_LE_RECONN == TRUE) if (p_cb->status == BTA_HH_OK) { bta_hh_le_add_dev_bg_conn(p_cb, TRUE);//加入到bg conn,用於回連 } #endif } }
關於註冊input notification 的部分,這裏就不細講了,咱們看看 狀態機的轉換:
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; #if BTA_HH_DEBUG == TRUE tBTA_HH_STATE in_state ; UINT16 debug_event = event; #endif 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); } ...
能夠看到,下一個狀態是 connection 狀態:
/* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST }
執行的行爲是:BTA_HH_OPEN_CMPL_ACT:
/******************************************************************************* ** ** Function bta_hh_open_cmpl_act ** ** Description HID host connection completed ** ** ** Returns void ** *******************************************************************************/ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { tBTA_HH_CONN conn ; UINT8 dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : \ p_cb->hid_handle; memset((void *)&conn, 0, sizeof (tBTA_HH_CONN)); conn.handle = dev_handle; bdcpy(conn.bda, p_cb->addr); /* increase connection number */ bta_hh_cb.cnt_num ++; /* initialize device driver */ bta_hh_co_open(p_cb->hid_handle, p_cb->sub_class, p_cb->attr_mask, p_cb->app_id);//打開uhid驅動,而且初始化線程btif_hh_poll_event_thread conn.status = p_cb->status; conn.le_hid = p_cb->is_le_device; conn.scps_supported = p_cb->scps_supported; /* set protocol mode when not default report mode */ if ( p_cb->mode != BTA_HH_PROTO_RPT_MODE && !p_cb->is_le_device) { ... } else (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn);//繼續上報bta_hh_cb.p_cback = bte_hh_evt p_cb->incoming_conn = FALSE; p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE; }
初始化驅動的部分,這裏不講了,,咱們繼續看看 狀態上報的流程:callback 實際執行流程是btif_hh_upstreams_evt,咱們繼續分析:
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; ... case BTA_HH_OPEN_EVT: if (p_data->conn.status == BTA_HH_OK) { p_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle); if (p_dev == NULL) { ... } else if (p_dev->fd < 0) { ... } else { btif_hh_cb.status = BTIF_HH_DEV_CONNECTED; // Send set_idle if the peer_device is a keyboard if (check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_KEYBOARD )|| check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_COMBO)) BTA_HhSetIdle(p_data->conn.handle, 0); btif_hh_cb.p_curr_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle); BTA_HhGetDscpInfo(p_data->conn.handle);//這裏涉及到將despinfo 發送到kernel,邏輯簡單這裏不作具體分析 p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status);//上報到JNI 層面
上面的流程主要就是 上報狀態,最後會上報到JNI 層面,最後確定會通知到framework。
hogp的流程分析到這裏就結束了。