本文介绍Bluedroid HFP的相关流程和知识点。
设备和手机连接之后,设备主动调用协议栈的接口去连接手机HFP。
1 btif_hf_client_get_interface()->connect();
协议栈上下文切换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 bt_status_t connect (RawAddress* bd_addr) { CHECK_BTHF_CLIENT_INIT(); return btif_queue_connect(UUID_SERVCLASS_HF_HANDSFREE, bd_addr, connect_int); } bt_status_t btif_queue_connect (uint16_t uuid, const RawAddress* bda, btif_connect_cb_t connect_cb) { connect_node_t node; memset (&node, 0 , sizeof (connect_node_t )); node.bda = *bda; node.uuid = uuid; node.connect_cb = connect_cb; return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT, (char *)&node, sizeof (connect_node_t ), NULL ); } bt_status_t btif_transfer_context (tBTIF_CBACK* p_cback, uint16_t event, char * p_params, int param_len, tBTIF_COPY_CBACK* p_copy_cback) { tBTIF_CONTEXT_SWITCH_CBACK* p_msg = (tBTIF_CONTEXT_SWITCH_CBACK*)osi_malloc( sizeof (tBTIF_CONTEXT_SWITCH_CBACK) + param_len); p_msg->hdr.event = BT_EVT_CONTEXT_SWITCH_EVT; p_msg->p_cb = p_cback; p_msg->event = event; if (p_copy_cback) { p_copy_cback(event, p_msg->p_param, p_params); } else if (p_params) { memcpy (p_msg->p_param, p_params, param_len); } btif_sendmsg(p_msg); return BT_STATUS_SUCCESS; } void btif_sendmsg (void * p_msg) { do_in_jni_thread(base::Bind(&bt_jni_msg_ready, p_msg)); } bt_status_t do_in_jni_thread (const base::Closure& task) { return do_in_jni_thread(FROM_HERE, task); } bt_status_t do_in_jni_thread (const tracked_objects::Location& from_here, const base::Closure& task) { if (!message_loop_ || !message_loop_->task_runner().get()) { return BT_STATUS_FAIL; } if (message_loop_->task_runner()->PostTask(from_here, task)) return BT_STATUS_SUCCESS; return BT_STATUS_FAIL; }
bt_jni_msg_ready() 这个作业会在btif上下文执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static void bt_jni_msg_ready (void * context) { BT_HDR* p_msg = (BT_HDR*)context; switch (p_msg->event) { case BT_EVT_CONTEXT_SWITCH_EVT: btif_context_switched(p_msg); break ; default : break ; } osi_free(p_msg); } static void btif_context_switched (void * p_msg) { tBTIF_CONTEXT_SWITCH_CBACK* p = (tBTIF_CONTEXT_SWITCH_CBACK*)p_msg; if (p->p_cb) p->p_cb(p->event, p->p_param); }
因此,BTIF_QUEUE_CONNECT_EVT这个事件,会在btif线程里,由queue_int_handle_evt()这个方法来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 static void queue_int_handle_evt (uint16_t event, char * p_param) { switch (event) { case BTIF_QUEUE_CONNECT_EVT: queue_int_add((connect_node_t *)p_param); break ; case BTIF_QUEUE_ADVANCE_EVT: queue_int_advance(); break ; case BTIF_QUEUE_CLEANUP_EVT: queue_int_cleanup((uint16_t *)(p_param)); return ; } if (stack_manager_get_interface()->get_stack_is_running()) btif_queue_connect_next(); } bt_status_t btif_queue_connect_next (void ) { if (!connect_queue || list_is_empty(connect_queue)) return BT_STATUS_FAIL; connect_node_t * p_head = (connect_node_t *)list_front(connect_queue); if (p_head->busy) return BT_STATUS_SUCCESS; p_head->busy = true ; return p_head->connect_cb(&p_head->bda, p_head->uuid); }
小结:
给应用层的方法,基本都是会转发到btif上下文来执行。这样有一个好处,就是由多线程调用变成单线程处理,在btif层就可以减少很多并发保护的代码,让系统更加简单可靠。
发送方申请xiaoxi内存,接收方释放消息内存。
所有的connect也是排队执行的,简化了应用场景。
发起HF Client连接 前面我们看到,传进来的连接方法是connect_int(),而UUID是UUID_SERVCLASS_HF_HANDSFREE。
现在,在btif_task调用connect_int()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static bt_status_t connect_int (RawAddress* bd_addr, uint16_t uuid) { btif_hf_client_cb_t * cb = btif_hf_client_allocate_cb(); if (cb == NULL ) { return BT_STATUS_BUSY; } cb->peer_bda = *bd_addr; if (is_connected(cb)) return BT_STATUS_BUSY; cb->state = BTHF_CLIENT_CONNECTION_STATE_CONNECTING; cb->peer_bda = *bd_addr; BTA_HfClientOpen(cb->peer_bda, BTIF_HF_CLIENT_SECURITY, &cb->handle); return BT_STATUS_SUCCESS; }
在btif上下文调用bta的api是正确的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void BTA_HfClientOpen (const RawAddress& bd_addr, tBTA_SEC sec_mask, uint16_t * p_handle) { tBTA_HF_CLIENT_API_OPEN* p_buf = (tBTA_HF_CLIENT_API_OPEN*)osi_malloc(sizeof (tBTA_HF_CLIENT_API_OPEN)); if (!bta_hf_client_allocate_handle(bd_addr, p_handle)) { APPL_TRACE_ERROR("%s: could not allocate handle" , __func__); return ; } p_buf->hdr.event = BTA_HF_CLIENT_API_OPEN_EVT; p_buf->hdr.layer_specific = *p_handle; p_buf->bd_addr = bd_addr; p_buf->sec_mask = sec_mask; bta_sys_sendmsg(p_buf); }
BTA_HfClientOpen()仅仅是调用bta_sys_sendmsg()把事件传到了btu_task。可见 btif 真的只是interface而已 。
bta_sys_sendmsg()则是post消息到btu的loop,到btu_task执行,处理函数是bta_sys_event(),它定义在bta_sys_main.cc中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void bta_sys_event (BT_HDR* p_msg) { uint8_t id; bool freebuf = true ; id = (uint8_t )(p_msg->event >> 8 ); if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL )) { freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg); } else { APPL_TRACE_WARNING("%s: Received unregistered event id %d" , __func__, id); } if (freebuf) { osi_free(p_msg); } }
我们看消息的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 enum { BTA_HF_CLIENT_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HS), BTA_HF_CLIENT_API_CLOSE_EVT, ... BTA_HF_CLIENT_API_ENABLE_EVT, BTA_HF_CLIENT_API_DISABLE_EVT }; #define BTA_SYS_EVT_START(id) ((id) << 8)
而 HF Client 注册的函数是bta_hf_client_reg():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 tBTA_STATUS BTA_HfClientEnable (tBTA_HF_CLIENT_CBACK* p_cback, tBTA_SEC sec_mask, tBTA_HF_CLIENT_FEAT features, const char * p_service_name) { return bta_hf_client_api_enable(p_cback, sec_mask, features, p_service_name); } tBTA_STATUS bta_hf_client_api_enable (tBTA_HF_CLIENT_CBACK* p_cback, tBTA_SEC sec_mask, tBTA_HF_CLIENT_FEAT features, const char * p_service_name) { ... bta_sys_register(BTA_ID_HS, &bta_hf_client_reg); ... return BTA_SUCCESS; } static const tBTA_SYS_REG bta_hf_client_reg = {bta_hf_client_hdl_event, BTA_HfClientDisable};
因此,BTA_HF_CLIENT_API_OPEN_EVT 这个event,会在btu_task中由bta_hf_client_hdl_event()处理。
除了 enable 和 disable 之外的事件,都会被传给 bta hf client 状态机处理。
Bluedroid的状态机是整个蓝牙协议栈的核心驱动力。它的实现比较简单,但却非常有效!
状态变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void bta_hf_client_start_open (tBTA_HF_CLIENT_DATA* p_data) { tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); if (client_cb == NULL ) { return ; } if (p_data) { client_cb->peer_addr = p_data->api_open.bd_addr; client_cb->cli_sec_mask = p_data->api_open.sec_mask; } RawAddress pending_bd_addr; if (PORT_IsOpening(pending_bd_addr)) { bta_hf_client_collision_cback(0 , BTA_ID_HS, 0 , &client_cb->peer_addr); return ; } client_cb->role = BTA_HF_CLIENT_INT; bta_hf_client_do_disc(client_cb); }
要连接对端手机的handsfree服务,首先需要查找对端是否有ag的服务。
从设备的hci来看,也可以看到,设备先发起sdp,查找hf_ag的服务。
开始查找之前,我们看下如果连接冲突,是怎么处理的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void bta_hf_client_collision_cback (UNUSED_ATTR tBTA_SYS_CONN_STATUS status, uint8_t id, UNUSED_ATTR uint8_t app_id, const RawAddress* peer_addr) { tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_bda(*peer_addr); if (client_cb != NULL && client_cb->state == BTA_HF_CLIENT_OPENING_ST) { client_cb->state = BTA_HF_CLIENT_INIT_ST; if (client_cb->p_disc_db) { (void )SDP_CancelServiceSearch(client_cb->p_disc_db); bta_hf_client_free_db(NULL ); } alarm_set_on_mloop(client_cb->collision_timer, BTA_HF_CLIENT_COLLISION_TIMER_MS, bta_hf_client_collision_timer_cback, (void *)client_cb); } }
可以看到,如果冲突了,则停止之前的连接,延时一会儿之后,我们重新发起连接。(为何是这个机制?不懂)
现在回到sdp查找服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void bta_hf_client_do_disc (tBTA_HF_CLIENT_CB* client_cb) { ... if (client_cb->role == BTA_HF_CLIENT_INT) { attr_list[0 ] = ATTR_ID_SERVICE_CLASS_ID_LIST; attr_list[1 ] = ATTR_ID_PROTOCOL_DESC_LIST; attr_list[2 ] = ATTR_ID_BT_PROFILE_DESC_LIST; attr_list[3 ] = ATTR_ID_SUPPORTED_FEATURES; num_attr = 4 ; uuid_list[0 ].uu.uuid16 = UUID_SERVCLASS_AG_HANDSFREE; } else { ... } client_cb->p_disc_db = (tSDP_DISCOVERY_DB*)osi_malloc(BT_DEFAULT_BUFFER_SIZE); uuid_list[0 ].len = LEN_UUID_16; uuid_list[1 ].len = LEN_UUID_16; db_inited = SDP_InitDiscoveryDb(client_cb->p_disc_db, BT_DEFAULT_BUFFER_SIZE, num_uuid, uuid_list, num_attr, attr_list); if (db_inited) { db_inited = SDP_ServiceSearchAttributeRequest2( client_cb->peer_addr, client_cb->p_disc_db, bta_hf_client_sdp_cback, (void *)client_cb); } ... }
准备db
发起服务发现请求
sdp是基于l2cap标准通道做的,因此它需要先建立l2cap连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 bool SDP_ServiceSearchAttributeRequest2 (const RawAddress& p_bd_addr, tSDP_DISCOVERY_DB* p_db, tSDP_DISC_CMPL_CB2* p_cb2, void * user_data) { tCONN_CB* p_ccb; p_ccb = sdp_conn_originate(p_bd_addr); if (!p_ccb) return (false ); p_ccb->disc_state = SDP_DISC_WAIT_CONN; p_ccb->p_db = p_db; p_ccb->p_cb2 = p_cb2; p_ccb->is_attr_search = true ; p_ccb->user_data = user_data; return (true ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 tCONN_CB* sdp_conn_originate (const RawAddress& p_bd_addr) { tCONN_CB* p_ccb; uint16_t cid; p_ccb = sdpu_allocate_ccb(); SDP_TRACE_EVENT("SDP - Originate started" ); p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; p_ccb->device_address = p_bd_addr; p_ccb->con_state = SDP_STATE_CONN_SETUP; cid = L2CA_ConnectReq(SDP_PSM, p_bd_addr); if (cid != 0 ) { p_ccb->connection_id = cid; return (p_ccb); } else { sdpu_release_ccb(p_ccb); return (NULL ); } }
小结:
hf client是基于状态机运行的,几乎所有profile都是
hf client连接前,需要先sdp查找对端ag服务
sdp是基于l2cap的,因此要先建立l2cap连接
L2CAP连接 两个设备想要发起SDP交互,首先需要建立L2CAP连接“
1 2 3 uint16_t L2CA_ConnectReq (uint16_t psm, const RawAddress& p_bd_addr) { return L2CA_ErtmConnectReq(psm, p_bd_addr, NULL ); }
这个函数里面的第一个参数是 psm(Protocol/ServiceMultiplexer ),这里简单介绍一下psm的概念:
psm是通道服用,它用来指定再L2CAP上通信的更高层面的协议类型
相同的高层级的多个实例,可能使用不同的L2CAP通道,但它们可能使用相同的PSM的值
比如,rfcomm的多个通道可能使用相同的psm,但每个通道的cid可能是不同的。
这些是系统注册的psm,也叫做内置psm。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define BT_PSM_SDP 0x0001 #define BT_PSM_RFCOMM 0x0003 #define BT_PSM_TCS 0x0005 #define BT_PSM_CTP 0x0007 #define BT_PSM_BNEP 0x000F #define BT_PSM_HIDC 0x0011 #define BT_PSM_HIDI 0x0013 #define BT_PSM_UPNP 0x0015 #define BT_PSM_AVCTP 0x0017 #define BT_PSM_AVDTP 0x0019 #define BT_PSM_AVCTP_13 0x001B #define BT_PSM_UDI_CP \ 0x001D #define BT_PSM_ATT 0x001F
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 uint16_t L2CA_ErtmConnectReq (uint16_t psm, const RawAddress& p_bd_addr, tL2CAP_ERTM_INFO* p_ertm_info) { ... if (!BTM_IsDeviceUp()) { return (0 ); } p_rcb = l2cu_find_rcb_by_psm(psm); if (p_rcb == NULL ) { return (0 ); } p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR); if (p_lcb == NULL ) { p_lcb = l2cu_allocate_lcb(p_bd_addr, false , BT_TRANSPORT_BR_EDR); if ((p_lcb == NULL ) || (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == false )) { return (0 ); } } p_ccb = l2cu_allocate_ccb(p_lcb, 0 ); if (p_ccb == NULL ) { return (0 ); } p_ccb->p_rcb = p_rcb; if (p_ertm_info) { ... } if (p_lcb->link_state == LST_CONNECTED) { l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL ); } .. return (p_ccb->local_cid); }
检查蓝牙是否打开
检查是否有link
检查psm是否是提前注册好的
申请ccb
连接channel
最终走到l2c_csm_execute,交给l2cap的状态机处理。
接着,发起方和响应方会检查security的要求,如果不满足则可能还需要发起配对。
security满足要求之后,正式进行l2cap的通道连接,并对通道进行配置,最后才能开启数据传输。
L2CAP的状态机这里暂不分析,最后会在CST_CONFIG状态下收到L2CEVT_L2CAP_CONFIG_RSP事件,从而状态机转到CST_OPEN状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static void l2c_csm_config (tL2C_CCB* p_ccb, uint16_t event, void * p_data) { ... switch (event) { ... case L2CEVT_L2CAP_CONFIG_RSP: l2cu_process_peer_cfg_rsp(p_ccb, p_cfg); if (p_cfg->result != L2CAP_CFG_PENDING) { ... if (p_ccb->config_done & IB_CFG_DONE) { ... p_ccb->chnl_state = CST_OPEN; ... } } (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg); break ; ... } }
这里的pL2CA_ConfigCfm_Cb是sdp_init时注册的reg_info,回调函数是sdp_config_cfm()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void sdp_init (void ) { ... sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind; sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm; sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL ; sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind; sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm; sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind; sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm; sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL ; sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind; sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL ; sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL ; if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info)) { SDP_TRACE_ERROR("SDP Registration failed" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void sdp_config_cfm (uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg) { ... if (p_cfg->result == L2CAP_CFG_OK) { p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE; if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE) { p_ccb->con_state = SDP_STATE_CONNECTED; if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { sdp_disc_connected(p_ccb); } else { alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, sdp_conn_timer_timeout, p_ccb); } } } else { ... } }
SDP服务发现 1 2 3 4 5 6 7 8 9 10 11 void sdp_disc_connected (tCONN_CB* p_ccb) { if (p_ccb->is_attr_search) { p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR; process_service_search_attr_rsp(p_ccb, NULL , NULL ); } else { p_ccb->num_handles = 0 ; sdp_snd_service_search_req(p_ccb, 0 , NULL ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 static void process_service_search_attr_rsp (tCONN_CB* p_ccb, uint8_t * p_reply, uint8_t * p_reply_end) { uint8_t *p, *p_start, *p_end, *p_param_len; uint8_t type; uint32_t seq_len; uint16_t param_len, lists_byte_count = 0 ; bool cont_request_needed = false ; ... if ((cont_request_needed) || (!p_reply)) { BT_HDR* p_msg = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); uint8_t * p; p_msg->offset = L2CAP_MIN_OFFSET; p = p_start = (uint8_t *)(p_msg + 1 ) + L2CAP_MIN_OFFSET; UINT8_TO_BE_STREAM(p, SDP_PDU_SERVICE_SEARCH_ATTR_REQ); UINT16_TO_BE_STREAM(p, p_ccb->transaction_id); p_ccb->transaction_id++; p_param_len = p; p += 2 ; #if (SDP_BROWSE_PLUS == TRUE) p = sdpu_build_uuid_seq(p, 1 , &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]); #else p = sdpu_build_uuid_seq(p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters); #endif UINT16_TO_BE_STREAM(p, sdp_cb.max_attr_list_size); if (p_ccb->p_db->num_attr_filters) p = sdpu_build_attrib_seq(p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters); else p = sdpu_build_attrib_seq(p, NULL , 0 ); if (p_reply) { if ((p_reply + *p_reply + 1 ) <= p_reply_end) { memcpy (p, p_reply, *p_reply + 1 ); p += *p_reply + 1 ; } else { android_errorWriteLog(0x534e4554 , "68161546" ); } } else UINT8_TO_BE_STREAM(p, 0 ); param_len = p - p_param_len - 2 ; UINT16_TO_BE_STREAM(p_param_len, param_len); p_msg->len = p - p_start; L2CA_DataWrite(p_ccb->connection_id, p_msg); alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, sdp_conn_timer_timeout, p_ccb); return ; }
这里构造了真正的搜索服务请求包,然后把请求包通过L2CAP发送了出去。
而接着手机回复了我们搜索服务的应答包。
我们接收对端发过来的sdp的应答,l2cap是通过sdp_data_ind()回调传过来的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static void sdp_data_ind (uint16_t l2cap_cid, BT_HDR* p_msg) { tCONN_CB* p_ccb; p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb != NULL ) { if (p_ccb->con_state == SDP_STATE_CONNECTED) { if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) sdp_disc_server_rsp(p_ccb, p_msg); else sdp_server_handle_client_req(p_ccb, p_msg); } else { ... } } else { ... } osi_free(p_msg); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void sdp_disc_server_rsp (tCONN_CB* p_ccb, BT_HDR* p_msg) { uint8_t *p, rsp_pdu; bool invalid_pdu = true ; ... switch (rsp_pdu) { ... case SDP_PDU_SERVICE_SEARCH_ATTR_RSP: if (p_ccb->disc_state == SDP_DISC_WAIT_SEARCH_ATTR) { process_service_search_attr_rsp(p_ccb, p, p_end); invalid_pdu = false ; } break ; } if (invalid_pdu) { sdp_disconnect(p_ccb, SDP_GENERIC_ERROR); } }
process_service_search_attr_rsp这个函数我们在前面看到一次了,但前面是作为发起的时候,现在是收到应答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 static void process_service_search_attr_rsp (tCONN_CB* p_ccb, uint8_t * p_reply, uint8_t * p_reply_end) { ... if (p_reply) { if (p_reply + 4 + sizeof (lists_byte_count) > p_reply_end) { android_errorWriteLog(0x534e4554 , "79884292" ); sdp_disconnect(p_ccb, SDP_INVALID_PDU_SIZE); return ; } p_reply += 4 ; BE_STREAM_TO_UINT16(lists_byte_count, p_reply); if ((p_ccb->list_len + lists_byte_count) > SDP_MAX_LIST_BYTE_COUNT) { sdp_disconnect(p_ccb, SDP_INVALID_PDU_SIZE); return ; } if (p_reply + lists_byte_count + 1 > p_reply_end) { android_errorWriteLog(0x534e4554 , "79884292" ); sdp_disconnect(p_ccb, SDP_INVALID_PDU_SIZE); return ; } if (p_ccb->rsp_list == NULL ) p_ccb->rsp_list = (uint8_t *)osi_malloc(SDP_MAX_LIST_BYTE_COUNT); memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, lists_byte_count); p_ccb->list_len += lists_byte_count; p_reply += lists_byte_count; if (*p_reply) { if (*p_reply > SDP_MAX_CONTINUATION_LEN) { sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE); return ; } cont_request_needed = true ; } } ... p = &p_ccb->rsp_list[0 ]; type = *p++; if ((type >> 3 ) != DATA_ELE_SEQ_DESC_TYPE) { return ; } p = sdpu_get_len_from_type(p, p + p_ccb->list_len, type, &seq_len); if (p == NULL || (p + seq_len) > (p + p_ccb->list_len)) { sdp_disconnect(p_ccb, SDP_ILLEGAL_PARAMETER); return ; } p_end = &p_ccb->rsp_list[p_ccb->list_len]; if ((p + seq_len) != p_end) { sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE); return ; } while (p < p_end) { p = save_attr_seq(p_ccb, p, &p_ccb->rsp_list[p_ccb->list_len]); if (!p) { sdp_disconnect(p_ccb, SDP_DB_FULL); return ; } } sdp_disconnect(p_ccb, SDP_SUCCESS); }
提取attr信息
判断是否需要连续请求,如果需要则继续请求
连续保存attr属性
如果全部信息都拿到并保存好,则断开sdp连接
RFCOMM连接 HFP是基于RFCOMM的,找到对端的AG服务后,就要开始进行RFCOMM的连接了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void sdp_disconnect (tCONN_CB* p_ccb, uint16_t reason) { ... if (p_ccb->connection_id != 0 ) { L2CA_DisconnectReq(p_ccb->connection_id); p_ccb->disconnect_reason = reason; } if (p_ccb->con_state == SDP_STATE_CONN_SETUP) { if (p_ccb->p_cb) (*p_ccb->p_cb)(reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)(reason, p_ccb->user_data); sdpu_release_ccb(p_ccb); } }
在sdp_disconnect的时候,会回调告诉用户(这里的sdp的user是指的hfp)。这个cb是在bta_hf_client_do_disc()的时候传入的bta_hf_client_sdp_cback():
1 2 3 4 5 6 7 8 9 10 void bta_hf_client_do_disc (tBTA_HF_CLIENT_CB* client_cb) { ... if (db_inited) { db_inited = SDP_ServiceSearchAttributeRequest2( client_cb->peer_addr, client_cb->p_disc_db, bta_hf_client_sdp_cback, (void *)client_cb); } ... }
在回调中,转发消息到btu_task进行处理,处理函数为bta_hf_client_hdl_event。而这个事件,会传到hf_client的状态机。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static void bta_hf_client_sdp_cback (uint16_t status, void * data) { uint16_t event; tBTA_HF_CLIENT_DISC_RESULT* p_buf = (tBTA_HF_CLIENT_DISC_RESULT*)osi_malloc( sizeof (tBTA_HF_CLIENT_DISC_RESULT)); tBTA_HF_CLIENT_CB* client_cb = (tBTA_HF_CLIENT_CB*)data; if (client_cb->role == BTA_HF_CLIENT_ACP) event = BTA_HF_CLIENT_DISC_ACP_RES_EVT; else event = BTA_HF_CLIENT_DISC_INT_RES_EVT; p_buf->hdr.event = event; p_buf->hdr.layer_specific = client_cb->handle; p_buf->status = status; bta_sys_sendmsg(p_buf); }
状态机驱动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void bta_hf_client_disc_int_res (tBTA_HF_CLIENT_DATA* p_data) { uint16_t event = BTA_HF_CLIENT_DISC_FAIL_EVT; ... if (p_data->disc_result.status == SDP_SUCCESS || p_data->disc_result.status == SDP_DB_FULL) { if (bta_hf_client_sdp_find_attr(client_cb)) { event = BTA_HF_CLIENT_DISC_OK_EVT; } } bta_hf_client_free_db(p_data); bta_hf_client_sm_execute(event, p_data); }
bta_hf_client_sdp_find_attr这个函数里面是从之前sdp保存在bta_hf_client_cb.scb.p_disc_db里面的结果,提取出来,保存在bta_hf_client_cb这个全局结构体中。
状态机驱动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void bta_hf_client_rfc_do_open (tBTA_HF_CLIENT_DATA* p_data) { tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); ... BTM_SetSecurityLevel(true , "" , BTM_SEC_SERVICE_HF_HANDSFREE, client_cb->cli_sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, client_cb->peer_scn); if (RFCOMM_CreateConnection(UUID_SERVCLASS_HF_HANDSFREE, client_cb->peer_scn, false , BTA_HF_CLIENT_MTU, client_cb->peer_addr, &(client_cb->conn_handle), bta_hf_client_mgmt_cback) == PORT_SUCCESS) { bta_hf_client_setup_port(client_cb->conn_handle); } ... } void bta_hf_client_setup_port (uint16_t handle) { PORT_SetEventMask(handle, PORT_EV_RXCHAR); PORT_SetEventCallback(handle, bta_hf_client_port_cback); }
到这里,正式开启RFCOMM的连接,然后把这个port绑定给hf_client。
从HCI可以看到,发现服务之后,就开始对通道进行认证加密,然后进行RFCOMM(channel3)的连接。
RFCOMM_CreateConnection这个函数最后的一个参数是bta_hf_client_mgmt_cback,RFCOMM连接成功后,会回调该函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static void bta_hf_client_mgmt_cback (uint32_t code, uint16_t port_handle) { ... if (code == PORT_SUCCESS) { if (client_cb && port_handle == client_cb->conn_handle) { p_buf->hdr.event = BTA_HF_CLIENT_RFC_OPEN_EVT; } ... } ... p_buf->hdr.layer_specific = client_cb != NULL ? client_cb->handle : 0 ; bta_sys_sendmsg(p_buf); }
状态机驱动:
current state:OPENING
event: BTA_HF_CLIENT_RFC_OPEN_EVT
action: bta_hf_client_rfc_open
next state: OPEN
1 2 3 4 5 6 7 8 9 10 void bta_hf_client_rfc_open (tBTA_HF_CLIENT_DATA* p_data) { tBTA_HF_CLIENT_CB* client_cb = bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific); bta_sys_conn_open(BTA_ID_HS, 1 , client_cb->peer_addr); bta_hf_client_slc_seq(client_cb, false ); }
RFCOMM打开成功之后,把该事件回调给bta,然后开启HFP SCL的建立流程。
1 2 3 4 5 6 7 8 9 10 void bta_sys_conn_open (uint8_t id, uint8_t app_id, const RawAddress& peer_addr) { if (bta_sys_cb.prm_cb) { bta_sys_cb.prm_cb(BTA_SYS_CONN_OPEN, id, app_id, &peer_addr); } if (bta_sys_cb.ppm_cb) { bta_sys_cb.ppm_cb(BTA_SYS_CONN_OPEN, id, app_id, &peer_addr); } }
另外值得指出的是,这一次,状态机从OPENING状态转变成了OPEN状态,而在hf client的状态机中,状态进入OPEN,会回调给app:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (in_state != client_cb->state) { tBTA_HF_CLIENT evt; memset (&evt, 0 , sizeof (evt)); evt.bd_addr = client_cb->peer_addr; if (client_cb->state == BTA_HF_CLIENT_INIT_ST) { bta_hf_client_app_callback(BTA_HF_CLIENT_CLOSE_EVT, &evt); } else if (client_cb->state == BTA_HF_CLIENT_OPEN_ST) { evt.open.handle = client_cb->handle; bta_hf_client_app_callback(BTA_HF_CLIENT_OPEN_EVT, &evt); } }
而BTA_HF_CLIENT_OPEN_EVT这个事件,会被抛到btif层,然后再回调给应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 static void btif_hf_client_upstreams_evt (uint16_t event, char * p_param) { tBTA_HF_CLIENT* p_data = (tBTA_HF_CLIENT*)p_param; btif_hf_client_cb_t * cb = btif_hf_client_get_cb_by_bda(p_data->bd_addr); if (cb == NULL && event == BTA_HF_CLIENT_OPEN_EVT) { cb = btif_hf_client_allocate_cb(); cb->handle = p_data->open.handle; cb->peer_bda = p_data->open.bd_addr; } ... switch (event) { case BTA_HF_CLIENT_OPEN_EVT: if (p_data->open.status == BTA_HF_CLIENT_SUCCESS) { cb->state = BTHF_CLIENT_CONNECTION_STATE_CONNECTED; cb->peer_feat = 0 ; cb->chld_feat = 0 ; } ... HAL_CBACK(bt_hf_client_callbacks, connection_state_cb, &cb->peer_bda, cb->state, 0 , 0 ); if (cb->state == BTHF_CLIENT_CONNECTION_STATE_DISCONNECTED) cb->peer_bda = RawAddress::kAny; if (p_data->open.status != BTA_HF_CLIENT_SUCCESS) btif_queue_advance(); break ; } }
可以看到,再RFCOMM建立连接后,第一次通过 connection_state_cb 回调给应用,接着开启SLC流程。
AT命令 接下来就是韩兆Hands-Free Profile进行AT命令的交互了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 void bta_hf_client_slc_seq (tBTA_HF_CLIENT_CB* client_cb, bool error) { ... switch (client_cb->at_cb.current_cmd) { case BTA_HF_CLIENT_AT_NONE: bta_hf_client_send_at_brsf(client_cb, bta_hf_client_cb_arr.features); break ; ... } } void bta_hf_client_send_at_brsf (tBTA_HF_CLIENT_CB* client_cb, tBTA_HF_CLIENT_FEAT features) { char buf[BTA_HF_CLIENT_AT_MAX_LEN]; int at_len; at_len = snprintf (buf, sizeof (buf), "AT+BRSF=%u\r" , features); bta_hf_client_send_at(client_cb, BTA_HF_CLIENT_AT_BRSF, buf, at_len); } static void bta_hf_client_send_at (tBTA_HF_CLIENT_CB* client_cb, tBTA_HF_CLIENT_AT_CMD cmd, const char * buf, uint16_t buf_len) { if ((client_cb->at_cb.current_cmd == BTA_HF_CLIENT_AT_NONE || client_cb->svc_conn == false ) && !alarm_is_scheduled(client_cb->at_cb.hold_timer)) { uint16_t len; client_cb->at_cb.current_cmd = cmd; if (!service_availability && (cmd == BTA_HF_CLIENT_AT_CNUM || cmd == BTA_HF_CLIENT_AT_COPS)) { APPL_TRACE_WARNING("%s: No service, skipping %d command" , __func__, cmd); bta_hf_client_handle_ok(client_cb); return ; } PORT_WriteData(client_cb->conn_handle, buf, buf_len, &len); bta_hf_client_start_at_resp_timer(client_cb); return ; } bta_hf_client_queue_at(client_cb, cmd, buf, buf_len); }
前面提到,bta_hf_client_setup_port把bta_hf_client_port_cback回调注册给rfcomm,rfcomm在该端口收到数据时,会回调该函数。
而bta_hf_client_port_cback有直接交给了状态机:
1 2 3 4 5 6 7 8 9 static void bta_hf_client_port_cback (UNUSED_ATTR uint32_t code, uint16_t port_handle) { ... tBTA_HF_CLIENT_RFC* p_buf = (tBTA_HF_CLIENT_RFC*)osi_malloc(sizeof (tBTA_HF_CLIENT_RFC)); p_buf->hdr.event = BTA_HF_CLIENT_RFC_DATA_EVT; p_buf->hdr.layer_specific = client_cb->handle; bta_sys_sendmsg(p_buf); }
状态机驱动:
current state:OPEN
event: BTA_HF_CLIENT_RFC_DATA_EVT
action: bta_hf_client_rfc_data
next state: OPEN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void bta_hf_client_rfc_data (tBTA_HF_CLIENT_DATA* p_data) { ... while (PORT_ReadData(client_cb->conn_handle, buf, BTA_HF_CLIENT_RFC_READ_MAX, &len) == PORT_SUCCESS) { if (len == 0 ) { break ; } bta_hf_client_at_parse(client_cb, buf, len); if (len < BTA_HF_CLIENT_RFC_READ_MAX) { break ; } } }
更多AT交互的细节,参考profile协议文档。
总结:
协议栈的实现中,基本没有看到锁的存在,这得益于它良好的分层设计,不同层级之间通过发消息和回调的方式进行交互,把跨层的交互集中到一起单线程处理
协议栈的实现依赖于它清晰可靠的状态机,状态机简化了我们的正常流程和异常处理
需要一个可靠的alarm超时机制,处理无线传输中的各种超时/失效异常
附录 - 常用AT命令 连接管理 连接建立 SLC,即sevice level connection,建立HFP SLC之前必须先建立RFCOMM通道。什么是SLC,SLC就是HFP HF和AG角色的一些AT command的交互,这些AT command交互完毕才叫HFP SLC建立。
图中虚线的部分是可选步骤,在SLC建立的时候可以不做。
比如AT+BAC=<HF Available Codecs>这个命令是做codec的协商,但如果supported feature中不支持codec协商,那么就没有这一步,从而使用默认的CVSD。
Supported feature exchange HF角色首先要发起AT+BRSF=<HF supported features>,AG会回复自己支持的特性。
Codec Negotiation 如果HF和AG都支持BRSF中的Codec协商,则HF需发送AT+BAC=<HF Available Codecs>告知AG自己所支持的codec类型。
在HFP规范中,CVSD(窄带)是强制支持的,mSBC(宽带)是可选的。
AG Indicators AG Indicators简单理解是希望AG把哪些信息告知HF。它包括几个流程:
(1)HF发送AT+CIND=? 询问AG支持的indicators(包括service/call/callsetup/callheld/signal/roam/battchg)
AG回复+CIND:...
(2)HF发送at+cind?询问各个indicators的状态
AG回复+CIND:...
(3)HF发送AT+CMER使能/禁止AG的indicators状态更新
(4)AG后续indicators有变化,可以通过+CIEV命令告知HF
AT+CHLD=? 如果HF&AG都支持三方通话,那么发送AT+CHLD=?询问手机三方通话支持的特性有哪些:
0 = 释放所有的hell calls 或者为一个waiting call 设置 User Determined User Busy (UDUB)
1 = 释放所有的active calls,接受另外的(held or waiting) call
1 = 挂断指定索引的active call
2 = 把所有的active calls 变成hold,然后接受另外的(held or waiting)call
2 = 除了idx的call外,其他全部都hold
3 = 增加一路通话
4 = Connects the two calls and disconnects the subscriber from both calls (连接两个电话并且断开两个电话的订阅)
HF Indicators ① 如果HF & AG都支持HF Indicators的feature,那么HF发送AT+BIND=<HF supported HF indicators>来告知AG支持哪些indicator
② 发送AT+BIND=?问询AG支持哪些indicator
③ 发送AT+BIND?问询AG哪些indicator是enable的
④ 发送AT+BIEV来使能某一个indicator
HFP的indicator一共有两个:
Enhanced Safety
Battery Level
连接释放 SLC的断开直接断开RFCOMM的链路就行了。
传输手机状态 AT+CMER,全局使能
AT+BIA,某一个indicator的active/deactive
使能indicator后,AG状态更新会发送 +CIEV 消息告知HF
信号强度:AG报告信号的强度
漫游状态:手机有没有入网,是否有插卡
电池电量:0-5
查询手机网络:查询手机的网络,比如“中国移动”
Report Extended Audio Gateway Error Results Code
如果有错误出现,AG会通过+CME ERROR:<err>发送给HF
Call 状态更新
incoming call, outgoing call,接听,挂断,没信号等场景都会触发
Call setup状态更新
来电、去电时会报告状态更新
Call held状态更新
0 = No calls held
1 = Call is placed on hold or active/held calls swapped
2 = Call on old, no active call
三方通话的时候经常会出现这种状态。
接听/挂断
使用ATA命令来接通电话。
另外注意一下CIEV命令上报call和callsetup的时机。
拨号
拨打电话
ATDdd...dd
语音信箱
ATD>nnn...
尾号重播
AT+BLDN
获取本地号码/获取通话号码
获取本机号码
HF: AT+CNUM
AG: +CNUM, <number>, <type>[,,<service>]
获取当前通话状态
AT+CLCC