1. 程式人生 > >Android4.4 BLE HOGP 裝置回連

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的貴賓了。

為什麼這麼說呢?看看下面的程式碼:

  1. /*******************************************************************************

  2. **

  3. ** Function bta_hh_le_open_cmpl

  4. **

  5. ** Description HID over GATT connection sucessfully opened

  6. **

  7. *******************************************************************************/

  8. void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb)

  9. {

  10. APPL_TRACE_DEBUG1("%s", __FUNCTION__);

  11. if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE)

  12. {

  13. #if BTA_HH_DEBUG

  14. bta_hh_le_hid_report_dbg(p_cb);

  15. #endif

  16. bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE);

  17. bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL);

  18. #if (BTA_HH_LE_RECONN == TRUE)

  19. if (p_cb->status == BTA_HH_OK)

  20. {

  21. bta_hh_le_add_dev_bg_conn(p_cb, TRUE);

  22. }

  23. #endif

  24. }

  25. }

這裡是協議簽訂後的處理。你看註釋上不是寫著“HID over GATT connection sucessfully opened”麼?

之所有稱這個slave即將成為我們的貴賓,因為上面的程式碼它呼叫bta_hh_le_add_dev_bg_conn,將這個HOGP device加到了表示whitelist的列表中。從此這個裝置可以隨時斷開藍芽鏈路,又隨時可以再連回來,只要隨便按個按鍵即可。是不是很任性?就像是高階會員才有的權利!看看它究竟做了點什麼

  1. 1.BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE);

  2. 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步中,它還會走到下面這段程式碼中:

  1. BOOLEAN btm_ble_resume_bg_conn(void)

  2. {

  3. tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;

  4. BOOLEAN ret = FALSE;

  5. if (p_cb->bg_conn_type != BTM_BLE_CONN_NONE)

  6. {

  7. if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO)

  8. ret = btm_ble_start_auto_conn(TRUE);

  9. if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE)

  10. ret = btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback);

  11. }

  12. return ret;

  13. }

看吧,第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:

  1. /*******************************************************************************

  2. **

  3. ** Function btm_ble_start_auto_conn

  4. **

  5. ** Description This function is to start/stop auto connection procedure.

  6. **

  7. ** Parameters start: TRUE to start; FALSE to stop.

  8. **

  9. ** Returns void

  10. **

  11. *******************************************************************************/

  12. BOOLEAN btm_ble_start_auto_conn(BOOLEAN start)

  13. {

  14. tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;

  15. BD_ADDR dummy_bda = {0};

  16. BOOLEAN exec = TRUE;

  17. UINT8 own_addr_type = BLE_ADDR_PUBLIC;

  18. UINT16 scan_int, scan_win;

  19. if (start)

  20. {

  21. if (p_cb->conn_state == BLE_CONN_IDLE && btm_ble_count_unconn_dev_in_whitelist() > 0)<span style="white-space:pre"> </span>//step 1

  22. {

  23. btm_execute_wl_dev_operation();<span style="white-space:pre"> </span>//step 2

  24. scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int;

  25. scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_SCAN_SLOW_WIN_1 : p_cb->scan_win;

  26. if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */

  27. scan_win, /* UINT16 scan_win */

  28. 0x01, /* UINT8 white_list */

  29. BLE_ADDR_PUBLIC, /* UINT8 addr_type_peer */

  30. dummy_bda, /* BD_ADDR bda_peer */

  31. own_addr_type, /* UINT8 addr_type_own, not allow random address for central */

  32. BTM_BLE_CONN_INT_MIN_DEF, /* UINT16 conn_int_min */

  33. BTM_BLE_CONN_INT_MAX_DEF, /* UINT16 conn_int_max */

  34. BTM_BLE_CONN_SLAVE_LATENCY_DEF, /* UINT16 conn_latency */

  35. BTM_BLE_CONN_TIMEOUT_DEF, /* UINT16 conn_timeout */

  36. 0, /* UINT16 min_len */

  37. 0)) /* UINT16 max_len */

  38. {

  39. /* start auto connection failed */

  40. exec = FALSE;

  41. }

  42. else

  43. {

  44. btm_ble_set_conn_st (BLE_BG_CONN);

  45. }

  46. }

  47. else

  48. {

  49. exec = FALSE;

  50. }

  51. }

  52. else

  53. {

  54. if (p_cb->conn_state == BLE_BG_CONN)

  55. {

  56. btsnd_hcic_ble_create_conn_cancel();

  57. btm_ble_set_conn_st (BLE_CONN_CANCEL);

  58. }

  59. }

  60. return exec;

  61. }

這裡我們不去關注回連使用的le create connection使用了那些引數,只看回連中呼叫的函式。

step 1:兩個判斷

第一個很明顯,我們之前設定過了。那第二個呢?插入程式碼:

  1. UINT8 btm_ble_count_unconn_dev_in_whitelist(void)

  2. {

  3. tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;

  4. UINT8 i, count = 0;

  5. for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM; i ++)

  6. {

  7. if (p_cb->bg_dev_list[i].in_use &&

  8. !BTM_IsAclConnectionUp(p_cb->bg_dev_list[i].bd_addr))

  9. {

  10. count ++;

  11. }

  12. }

  13. return count;

  14. }

你瞧,前面新增的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中去:

  1. /*******************************************************************************

  2. **

  3. ** Function btm_execute_wl_dev_operation

  4. **

  5. ** Description execute the pending whitelist device operation(loading or removing)

  6. *******************************************************************************/

  7. BOOLEAN btm_execute_wl_dev_operation(void)

  8. {

  9. tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q;

  10. UINT8 i = 0;

  11. BOOLEAN rt = TRUE;

  12. for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && rt; i ++, p_dev_op ++)

  13. {

  14. if (p_dev_op->in_use)

  15. {

  16. rt = btm_add_dev_to_controller(p_dev_op->to_add, p_dev_op->bd_addr, p_dev_op->attr);

  17. memset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP));

  18. }

  19. else

  20. break;

  21. }

  22. return rt;

  23. }

看看,這裡是呼叫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,情況又是如何呢?