Android4.4 BLE HOGP 裝置回連
好久沒寫東西了,今天回來看看,發現以前寫的還不算太死板。寫寫東西也正好理理思路。
廢話不多說,進入今天的主題——
Android4.4 環境下 BLE HOGP裝置的自動回連
何為BLE?子曰bluetooth low energy,也就是低功耗藍芽。BLE它是藍芽SIG後來提出來的,最早的藍芽是BR\EDR.。
何為HOGP?子曰hid over gatt,也就是基於GATT profile的hid。
那又為何要回連?一般的hid裝置為了方便使用,都不會去使用限制使用者操作的電源線來供電,而是使用電池。因此省電是對hid裝置的一個基本需求,誰願意帶著每過幾小時就得充電的藍芽手環?因此當裝置長時間不在使用時,藍芽的連線應該為了省電而斷開;而為了隨時都可以再次使用藍芽裝置,回連就顯得有必要,從而避免每次花費數十秒的重新建立連線的時間。其實原本藍芽的hid裝置都支援回連,只是那種情況是由hid device來主動連線。而BLE既然自稱低功耗,回連就更不可少了。
下面就從Android4.4的bluedroid中看看,BLE HOGP裝置的回連實現。
協議簽訂好,您就是我們的貴賓了!
首先,當底層acl鏈路建立起來後,作為HOGP slave的hid device和對端的master就要開始簽訂一些協議了(雖然稱作profile,但個人覺得就是些協議)。GATT需要協商,看看HOGP device都有哪些屬性(ATT,即Attribute);而HOGP則要看看你HOGP device的GATT屬性值如何,從而決定master該如何通過GATT來處理HOGP device傳送過來的訊息。當所有的協議都簽訂好後,這個角色名為slave的HOGP device就將成為master的貴賓了。
為什麼這麼說呢?看看下面的程式碼:
-
/*******************************************************************************
-
**
-
** 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)
-
{
-
APPL_TRACE_DEBUG1("%s", __FUNCTION__);
-
if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE)
-
{
-
#if BTA_HH_DEBUG
-
bta_hh_le_hid_report_dbg(p_cb);
-
#endif
-
bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE);
-
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);
-
}
-
#endif
-
}
-
}
這裡是協議簽訂後的處理。你看註釋上不是寫著“HID over GATT connection sucessfully opened”麼?
之所有稱這個slave即將成為我們的貴賓,因為上面的程式碼它呼叫bta_hh_le_add_dev_bg_conn,將這個HOGP device加到了表示whitelist的列表中。從此這個裝置可以隨時斷開藍芽鏈路,又隨時可以再連回來,只要隨便按個按鍵即可。是不是很任性?就像是高階會員才有的權利!看看它究竟做了點什麼
-
1.BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE);
-
2.BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL);
如果你看過經由GATT介面(而不是settings裡頭點選裝置配對)來建立連線的程式碼,你就會發現,它和background connection建立的過程類似,只是順序反了過來。這裡的第1步做了很多事兒,它將這個HOGP device先後加到了四個全域性的結構陣列中:
a)bta_gattc_cb.bg_track b)gatt_cb.bgconn_dev
c)btm_cb.ble_ctr_cb.bg_dev_list
d)btm_cb.ble_ctr_cb.wl_op_q
有什麼用呢?這裡僅僅是添加了,並沒有真正去執行新增到whitelist中的動作。不過,這裡的d)就有點類似host端whitelist的意思,後面會看到,只有在這個裡頭的HOGP device才會被新增到control(如果不知道host和control,請自覺去腦補藍芽spec)的whitelist中,controller才會主動連線該裝置。而c)項是另一個關於host端的whitelist記錄,它和d)配合使用,HOGP device才會真正的被回連。至於剩下的兩個,本文不會涉及到,不過這裡簡單提下。a)如其名是用來track的,我發現在出現某些bug時這個變數才會找到自己的存在感(至於是神馬bug,你可以試試多個HOGP device連線同一個master的情況);而b)是gatt層對“貴賓”的記錄,對於同一個HOGP device可以有不同gatt_if,但是whitelist只認藍芽地址,因此只需要在第一次見到這個device時將它設定到host端的whitelist中,有點像”既然您是貴賓,您的家人也是貴賓,我們就不再專門做記錄“的意思,不過我還沒見過實際的情況。
至於第2步,其實它啥也沒幹,就只是將btm_cb.ble_ctr_cb.bg_conn_type設定成了BTA_DM_BLE_CONN_AUTO,它原來應該是BTM_BLE_CONN_NONE。其實在第1步中,它還會走到下面這段程式碼中:
-
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);
-
if (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;
-
}
看吧,第1步它差點就走到了btm_ble_start_auto_conn,直接去執行le create connection了!不過還好,那時候的btm_cb.ble_ctr_cb.bg_conn_type為BTM_BLE_CONN_NONE。這裡提下個人的發現,btm_cb.ble_ctr_cb.bg_conn_type一旦被設定為BTA_DM_BLE_CONN_AUTO了,就再也不會變回BTM_BLE_CONN_NONE了。或許有人會問了,後來的HOGP device走到上面的程式碼會怎麼樣呢,會在連線已經建立的時候去傻乎乎的再次le create connection麼?答案是——你想多了。
我們提供優質的回連服務,您無需多慮!
走完上面兩步,一切就緒,就等你HOGP device發起disconnect了!不管你多任性,我們都提供優質的回連服務!看看我們如何應對disconnect:
==>btu_hcif_disconnection_comp_evt
==>btm_sec_disconnected
==>btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, p_dev_rec->bd_addr, FALSE)
==>btm_ble_resume_bg_conn
這裡再次回到這個函式,看來它和回連有很大的淵源,去看看被它呼叫的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;
-
UINT8 own_addr_type = BLE_ADDR_PUBLIC;
-
UINT16 scan_int, scan_win;
-
if (start)
-
{
-
if (p_cb->conn_state == BLE_CONN_IDLE && btm_ble_count_unconn_dev_in_whitelist() > 0)<span style="white-space:pre"> </span>//step 1
-
{
-
btm_execute_wl_dev_operation();<span style="white-space:pre"> </span>//step 2
-
scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int;
-
scan_win = (p_cb->scan_win == BTM_BLE_CONN_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 */
-
BLE_ADDR_PUBLIC, /* UINT8 addr_type_peer */
-
dummy_bda, /* BD_ADDR bda_peer */
-
own_addr_type, /* UINT8 addr_type_own, not allow random address for central */
-
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;
-
}
-
else
-
{
-
btm_ble_set_conn_st (BLE_BG_CONN);
-
}
-
}
-
else
-
{
-
exec = FALSE;
-
}
-
}
-
else
-
{
-
if (p_cb->conn_state == BLE_BG_CONN)
-
{
-
btsnd_hcic_ble_create_conn_cancel();
-
btm_ble_set_conn_st (BLE_CONN_CANCEL);
-
}
-
}
-
return exec;
-
}
這裡我們不去關注回連使用的le create connection使用了那些引數,只看回連中呼叫的函式。
step 1:兩個判斷
第一個很明顯,我們之前設定過了。那第二個呢?插入程式碼:
-
UINT8 btm_ble_count_unconn_dev_in_whitelist(void)
-
{
-
tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
-
UINT8 i, count = 0;
-
for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM; i ++)
-
{
-
if (p_cb->bg_dev_list[i].in_use &&
-
!BTM_IsAclConnectionUp(p_cb->bg_dev_list[i].bd_addr))
-
{
-
count ++;
-
}
-
}
-
return count;
-
}
你瞧,前面新增的btm_cb.ble_ctr_cb.bg_dev_list派上用場了吧。這裡數了數在btm_cb.ble_ctr_cb.bg_dev_list這個陣列中並且沒有acl鏈路(你看,它也不會傻到有acl鏈路還去再建)的裝置有多少個,將總數返回。既然前面新增過,這裡就一定返回大於0的結果。因此,兩條判斷pass,進入step 2。
step 2:
新增到貴賓列表——controller的whitelist中去:
-
/*******************************************************************************
-
**
-
** 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;
-
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, p_dev_op->attr);
-
memset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP));
-
}
-
else
-
break;
-
}
-
return rt;
-
}
看看,這裡是呼叫btm_add_dev_to_controller來將我們的HOGP device加到controller的whitelist中了。此外,被新增的裝置也被從btm_cb.ble_ctr_cb.wl_op_q中清除了。這裡頭還有疑問麼?(你以為我會告訴你,這裡把所有的device,不管連著還是斷開的,都加到whitelist中了麼?)
其實還有一個step 3的,就是上面呼叫btsnd_hcic_ble_create_ll_conn告訴controller使用whitelist來建立BLE鏈路。不過它太明顯了,就不多說了。
到了這裡,我們的服務員就已經準備好了,就等著HOGP device上門回連了。沒錯,是上門回連,因為master只有通過slave的advertisement才能找到HOGP device並重新建立BLE鏈路。只是這次,master有link key,也有gatt的cache和HOGP的cahce(也可能沒有,你可以猜猜看是什麼時候),因此省去了很大一塊兒SMP和GATT搜尋的時間。
服務評估:
說來說去,服務好不好都得看bluedroid程式碼設計的如何。如果顧及的情況不全面,那就會有問題。不過我們不該去吐槽bluedroid的提供者,它”偶然“留下的bug也讓我等碼農們的生活充實了不少,每次跟boss彙報都有了可觀的工作量。下面列幾條自動回連服務中存在的疑問:
1.一個HOGP裝置還好,多個裝置又會如何?
2.如果回連的時候master沒有gatt cache,那麼回連還快得起來麼?
3.HOGP device斷開鏈路master就該設定回連,那master想主動斷開device,情況又是如何呢?