在上一篇文章中,我們已經分析了:a2dp初始化流程 這篇文章主要分析a2dp的連接流程,其中還是涉及到一些底層的profile以及protocol,SDP、AVDTP以及L2CAP等。
當藍牙設備與主機配對完成之后,作為一個BREDR設備,會走SDP的流程進行服務搜索,當服務搜索完成之后,上層應用得到了該設備的相關的服務,將啟動相關的profile 的連接流程,如果對方是一個音箱設備,那么就會觸發a2dp的連接流程。我們就從開始調用a2dp connect的地方進行分析:
D/BluetoothA2dp( 3698): connect(C9:50:76:F2:3C:B6)
上面的log 的打印是在BluetoothA2dp.java 中,
public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device);//進行連接 } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; }
注:文章中涉及到 Android里面的通信機制,binder之類的機制,限於篇幅,暫時不講,重點講 代碼的執行流程。
這里的mService 就是A2dpService,其實現在package/app/bluetooth 下面,我們繼續看:
public boolean connect(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {//在上傳uuid的時候就已經設置了PRIORITY_ON return false; } ParcelUuid[] featureUuids = device.getUuids(); if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) && !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {//判斷是否支持sink Log.e(TAG,"Remote does not have A2dp Sink UUID"); return false; } int connectionState = mStateMachine.getConnectionState(device); if (connectionState == BluetoothProfile.STATE_CONNECTED || connectionState == BluetoothProfile.STATE_CONNECTING) { return false; } mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);//發送消息進行連接 return true; }
下面接着看 消息的處理,這個消息是發往A2dpStateMachine的,最開始是進入到Disconnected的狀態機,看看其處理:
switch(message.what) { case CONNECT: BluetoothDevice device = (BluetoothDevice) message.obj; broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); if (!connectA2dpNative(getByteAddress(device)) ) {//調用native 的connect broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); break; }
這里涉及到JNI的通信,其機制很簡單,限於篇幅也不講解了。其實現在com_android_com_android_bluetooh.cpp中:
static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) { jbyte *addr; bt_bdaddr_t * btAddr; bt_status_t status; ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface); if (!sBluetoothA2dpInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); btAddr = (bt_bdaddr_t *) addr; if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {//調用bluedroid里面的a2dp的connect接口 ALOGE("Failed HF connection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; }
到這里之后,連接的流程就直接進入到協議棧了,這之后就完全是C語言的代碼,a2dp的connect 接口實現在btif_av.c里面 ,我們繼續看,
static bt_status_t src_connect_sink(bt_bdaddr_t *bd_addr) { CHECK_BTAV_INIT(); return btif_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, bd_addr, connect_int); }
我們繼續看 btif_queue_connect 做了什么?從他的注釋中,我們看出他是先把建立連接的信息加入到queue中,並且觸發queue中的下一次的連接操作。
/******************************************************************************* ** ** Function btif_queue_connect ** ** Description Add a new connection to the queue and trigger the next ** scheduled connection. ** ** Returns BT_STATUS_SUCCESS if successful ** *******************************************************************************/ bt_status_t btif_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda, btif_connect_cb_t connect_cb) { connect_node_t node; memset(&node, 0, sizeof(connect_node_t)); memcpy(&node.bda, bda, sizeof(bt_bdaddr_t)); 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); }
把建立連接的事情 transfer到 btif task中,我們繼續看:
static void queue_int_handle_evt(UINT16 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; } if (stack_manager_get_interface()->get_stack_is_running()) btif_queue_connect_next();//觸發下一個連接 }
我們看看btif_queue_connect_next 的實現:
// This function dispatches the next pending connect request. It is called from // stack_manager when the stack comes up. 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 = list_front(connect_queue); // If the queue is currently busy, we return success anyway, // since the connection has been queued... if (p_head->busy) return BT_STATUS_SUCCESS; p_head->busy = true; return p_head->connect_cb(&p_head->bda, p_head->uuid);//執行隊列中第一個連接任務 }
看到上面的代碼,我們明白,a2dp的連接執行的函數就是connect_int(bd_addr,UUID_SERVCLASS_AUDIO_SOURCE) ,我們繼續分析這個函數:
/******************************************************************************* ** ** Function connect ** ** Description Establishes the AV signalling channel with the remote headset ** ** Returns bt_status_t ** *******************************************************************************/ static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid) { btif_av_connect_req_t connect_req; connect_req.target_bda = bd_addr; connect_req.uuid = uuid; btif_av_cb.uuid = uuid;//保存uuid:UUID_SERVCLASS_AUDIO_SOURCE btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char*)&connect_req);//放置到狀態表中讓其自動執行 return BT_STATUS_SUCCESS; }
當前的狀態是idle,
/***************************************************************************** ** ** Function btif_av_state_idle_handler ** ** Description State managing disconnected AV link ** ** Returns TRUE if event was processed, FALSE otherwise ** *******************************************************************************/ static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data) { switch (event) { ... case BTIF_AV_CONNECT_REQ_EVT: { if (event == BTIF_AV_CONNECT_REQ_EVT) { memcpy(&btif_av_cb.peer_bda, ((btif_av_connect_req_t*)p_data)->target_bda, sizeof(bt_bdaddr_t)); BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle, TRUE, BTA_SEC_AUTHENTICATE, ((btif_av_connect_req_t*)p_data)->uuid);//BTA_AvOpen } else if (event == BTA_AV_PENDING_EVT) { bdcpy(btif_av_cb.peer_bda.address, ((tBTA_AV*)p_data)->pend.bd_addr); BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle, TRUE, BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SOURCE); } btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING);//上報狀態 } break;
狀態改變,最終會上報到JNI層,這里就不分析了。下面我們重點分析BTA_AvOpen 流程,這個BTA_AvOpen 函數名字是不是有點眼熟?在a2dp初始化的流程中,出現的兩個函數是BTA_AvEnable和BTA_AvRegister,這里來分析其兄弟函數BTA_AvOpen,
/******************************************************************************* ** ** Function BTA_AvOpen ** ** Description Opens an advanced audio/video connection to a peer device. ** When connection is open callback function is called ** with a BTA_AV_OPEN_EVT. ** ** Returns void ** *******************************************************************************/ void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, BOOLEAN use_rc, tBTA_SEC sec_mask, UINT16 uuid) { tBTA_AV_API_OPEN *p_buf; if ((p_buf = (tBTA_AV_API_OPEN *) GKI_getbuf(sizeof(tBTA_AV_API_OPEN))) != NULL) { p_buf->hdr.event = BTA_AV_API_OPEN_EVT; p_buf->hdr.layer_specific = handle; bdcpy(p_buf->bd_addr, bd_addr); p_buf->use_rc = use_rc; p_buf->sec_mask = sec_mask; p_buf->switch_res = BTA_AV_RS_NONE; p_buf->uuid = uuid; bta_sys_sendmsg(p_buf); } }
發送BTA_AV_API_OPEN_EVT 時間到btu task.繼續分析:
根據我們之前的分析,這個event是在bta_av_ssm_execute 里面處理的:
/******************************************************************************* ** ** Function bta_av_ssm_execute ** ** Description Stream state machine event handling function for AV ** ** ** Returns void ** *******************************************************************************/ void bta_av_ssm_execute(tBTA_AV_SCB *p_scb, UINT16 event, tBTA_AV_DATA *p_data) { tBTA_AV_SST_TBL state_table; UINT8 action; int i, xx; ... /* look up the state table for the current state */ state_table = bta_av_sst_tbl[p_scb->state]; event -= BTA_AV_FIRST_SSM_EVT; /* set next state */ p_scb->state = state_table[event][BTA_AV_SNEXT_STATE]; /* execute action functions */ for(i=0; i< BTA_AV_SACTIONS; i++) { if ((action = state_table[event][i]) != BTA_AV_SIGNORE) { (*p_scb->p_act_tbl[action])(p_scb, p_data); } else break; } }
發現其處理思路都是差不多,先去查閱狀態轉換表,然后去執行,當前的stream state machine 的狀態是bta_av_sst_init
/* AP_OPEN_EVT */ {BTA_AV_DO_DISC, BTA_AV_SIGNORE, BTA_AV_OPENING_SST },
發現其執行的動作是BTA_AV_DO_DISC,下一個狀態是BTA_AV_OPENING_SST,執行的函數是bta_av_do_disc_a2d,發現現在做的操作是先搜索,我們看看其具體的實現:
/******************************************************************************* ** ** Function bta_av_do_disc_a2d ** ** Description Do service discovery for A2DP. ** ** Returns void ** *******************************************************************************/ void bta_av_do_disc_a2d (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { BOOLEAN ok_continue = FALSE; tA2D_SDP_DB_PARAMS db_params; UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_PROTOCOL_DESC_LIST, ATTR_ID_BT_PROFILE_DESC_LIST}; UINT16 sdp_uuid = 0; /* UUID for which SDP has to be done */ memcpy (&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN)); switch(p_data->api_open.switch_res) { case BTA_AV_RS_NONE: if (bta_av_switch_if_needed(p_scb) || !bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT)) { /* waiting for role switch result. save the api to control block */ memcpy(&p_scb->q_info.open, &p_data->api_open, sizeof(tBTA_AV_API_OPEN)); p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN; p_scb->q_tag = BTA_AV_Q_TAG_OPEN; } else { ok_continue = TRUE; } break; ... } ... /* store peer addr other parameters */ bta_av_save_addr(p_scb, p_data->api_open.bd_addr); p_scb->sec_mask = p_data->api_open.sec_mask; p_scb->use_rc = p_data->api_open.use_rc; bta_sys_app_open(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);//power manager相關,略過 /* allocate discovery database */ if (p_scb->p_disc_db == NULL) { p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) GKI_getbuf(BTA_AV_DISC_BUF_SIZE); } /* only one A2D find service is active at a time */ bta_av_cb.handle = p_scb->hndl; if(p_scb->p_disc_db) { /* set up parameters */ db_params.db_len = BTA_AV_DISC_BUF_SIZE; db_params.num_attr = 3; db_params.p_db = p_scb->p_disc_db; db_params.p_attrs = attr_list; p_scb->uuid_int = p_data->api_open.uuid; if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SINK) sdp_uuid = UUID_SERVCLASS_AUDIO_SOURCE; else if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) sdp_uuid = UUID_SERVCLASS_AUDIO_SINK; APPL_TRACE_DEBUG("uuid_int 0x%x, Doing SDP For 0x%x", p_scb->uuid_int, sdp_uuid); if(A2D_FindService(sdp_uuid, p_scb->peer_addr, &db_params, bta_av_a2d_sdp_cback) == A2D_SUCCESS)//具體搜索sink service { return; } } ... }
上面的流程很簡單,就是進行 sink service的搜索, 其實搜索的att list如下:
UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
ATTR_ID_PROTOCOL_DESC_LIST,
ATTR_ID_BT_PROFILE_DESC_LIST};
我們繼續看搜索服務的實現流程:
/****************************************************************************** ** ** Function A2D_FindService ** ** Description This function is called by a client application to ** perform service discovery and retrieve SRC or SNK SDP ** record information from a server. Information is ** returned for the first service record found on the ** server that matches the service UUID. The callback ** function will be executed when service discovery is ** complete. There can only be one outstanding call to ** A2D_FindService() at a time; the application must wait ** for the callback before it makes another call to ** the function. ** ** Input Parameters: ** service_uuid: Indicates SRC or SNK. ** ** bd_addr: BD address of the peer device. ** ** p_db: Pointer to the information to initialize ** the discovery database. ** ** p_cback: Pointer to the A2D_FindService() ** callback function. ** ** Output Parameters: ** None. ** ** Returns A2D_SUCCESS if function execution succeeded, ** A2D_INVALID_PARAMS if bad parameters are given. ** A2D_BUSY if discovery is already in progress. ** A2D_FAIL if function execution failed. ** ******************************************************************************/ tA2D_STATUS A2D_FindService(UINT16 service_uuid, BD_ADDR bd_addr, tA2D_SDP_DB_PARAMS *p_db, tA2D_FIND_CBACK *p_cback) { tSDP_UUID uuid_list; BOOLEAN result = TRUE; UINT16 a2d_attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, /* update A2D_NUM_ATTR, if changed */ ATTR_ID_BT_PROFILE_DESC_LIST, ATTR_ID_SUPPORTED_FEATURES, ATTR_ID_SERVICE_NAME, ATTR_ID_PROTOCOL_DESC_LIST, ATTR_ID_PROVIDER_NAME}; /* set up discovery database */ uuid_list.len = LEN_UUID_16; uuid_list.uu.uuid16 = service_uuid; if(p_db->p_attrs == NULL || p_db->num_attr == 0)//已經設置好了 { p_db->p_attrs = a2d_attr_list; p_db->num_attr = A2D_NUM_ATTR; } result = SDP_InitDiscoveryDb(p_db->p_db, p_db->db_len, 1, &uuid_list, p_db->num_attr, p_db->p_attrs);//初始化數據庫 if (result == TRUE) { /* store service_uuid and discovery db pointer */ a2d_cb.find.p_db = p_db->p_db; a2d_cb.find.service_uuid = service_uuid; a2d_cb.find.p_cback = p_cback; /* perform service search */ result = SDP_ServiceSearchAttributeRequest(bd_addr, p_db->p_db, a2d_sdp_cback);//執行service search if(FALSE == result) { a2d_cb.find.service_uuid = 0; } } return (result ? A2D_SUCCESS : A2D_FAIL); }
關於SDP_ServiceSearchAttributeRequest 這個函數在sdp 服務搜索流程 中已經詳細講過,這里不再詳細講解,搜索完成之后執行回調函數a2d_sdp_cback,我們看其實現:
/****************************************************************************** ** ** Function a2d_sdp_cback ** ** Description This is the SDP callback function used by A2D_FindService. ** This function will be executed by SDP when the service ** search is completed. If the search is successful, it ** finds the first record in the database that matches the ** UUID of the search. Then retrieves various parameters ** from the record. When it is finished it calls the ** application callback function. ** ** Returns Nothing. ** ******************************************************************************/ static void a2d_sdp_cback(UINT16 status) { tSDP_DISC_REC *p_rec = NULL; tSDP_DISC_ATTR *p_attr; BOOLEAN found = FALSE; tA2D_Service a2d_svc; tSDP_PROTOCOL_ELEM elem; if (status == SDP_SUCCESS) { /* loop through all records we found */ do { /* get next record; if none found, we're done */ if ((p_rec = SDP_FindServiceInDb(a2d_cb.find.p_db, a2d_cb.find.service_uuid, p_rec)) == NULL) { break; } memset(&a2d_svc, 0, sizeof(tA2D_Service)); /* get service name */ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) { a2d_svc.p_service_name = (char *) p_attr->attr_value.v.array; a2d_svc.service_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); } /* get provider name */ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_PROVIDER_NAME)) != NULL) { a2d_svc.p_provider_name = (char *) p_attr->attr_value.v.array; a2d_svc.provider_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); } /* get supported features */ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) { a2d_svc.features = p_attr->attr_value.v.u16; } /* get AVDTP version */ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_AVDTP, &elem)) { a2d_svc.avdt_version = elem.params[0]; A2D_TRACE_DEBUG("avdt_version: 0x%x", a2d_svc.avdt_version); } /* we've got everything, we're done */ found = TRUE; break; } while (TRUE); } a2d_cb.find.service_uuid = 0; /* return info from sdp record in app callback function */ if (a2d_cb.find.p_cback != NULL) { (*a2d_cb.find.p_cback)(found, &a2d_svc);//將獲取的數據保存在a2d_svc,然后再次調用回調函數bta_av_a2d_sdp_cback上報 } return; }
此函數就是處理 搜索的結果,然后再次調用bta_av_a2d_sdp_cback來上報結果,我們繼續分析這個回調函數,:
/******************************************************************************* ** ** Function bta_av_a2d_sdp_cback ** ** Description A2DP service discovery callback. ** ** Returns void ** *******************************************************************************/ static void bta_av_a2d_sdp_cback(BOOLEAN found, tA2D_Service *p_service) { tBTA_AV_SDP_RES *p_msg; tBTA_AV_SCB *p_scb; if ((p_msg = (tBTA_AV_SDP_RES *) GKI_getbuf(sizeof(tBTA_AV_SDP_RES))) != NULL) { p_msg->hdr.event = (found) ? BTA_AV_SDP_DISC_OK_EVT : BTA_AV_SDP_DISC_FAIL_EVT; p_scb = bta_av_hndl_to_scb(bta_av_cb.handle); if (p_scb) { if (found && (p_service != NULL)) p_scb->avdt_version = p_service->avdt_version; else p_scb->avdt_version = 0x00; p_msg->hdr.layer_specific = bta_av_cb.handle; bta_sys_sendmsg(p_msg); } } }
這里發現是向BTU 線程發送BTA_AV_SDP_DISC_OK_EVT 事件,那說明還不是直接在這個回調函數里面去處理的,我們繼續看BTA_AV_SDP_DISC_OK_EVT 的處理:
根據a2dp 初始化一文中的分析,他應該是由bta_av_ssm_execute 來處理,我們看看具體的處理:
AV Sevent(0x41)=0x1214(SDP_DISC_OK) state=2(OPENING)
狀態機的處理討論都是一樣的,這里不作細節分析:
/* SDP_DISC_OK_EVT */ {BTA_AV_CONNECT_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST },
發現狀態不變,下一個狀態還是BTA_AV_OPENING_SST,執行的動作是BTA_AV_CONNECT_REQ,那么看起來是要進行請求連接的操作的流程了,我們繼續看:
執行的函數是bta_av_connect_req:
/******************************************************************************* ** ** Function bta_av_connect_req ** ** Description Connect AVDTP connection. ** ** Returns void ** *******************************************************************************/ void bta_av_connect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { UNUSED(p_data); utl_freebuf((void **) &p_scb->p_disc_db); if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) { /* SNK initiated L2C connection while SRC was doing SDP. */ /* Wait until timeout to check if SNK starts signalling. */ APPL_TRACE_EVENT("bta_av_connect_req: coll_mask = 0x%2X", p_scb->coll_mask); return; } AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]);//進入到AVDTP的流程了 }
從函數名稱,我們可以發現,a2dp的連接其實是建立在AVDTP 的基礎之上的,下面執行到AVDTP 層面的連接了,另外這個函數的第三個參數是 用hdi 作為索引的回調函數,在一簇回調函數中,其實現邏輯是相同的,只是處理的pscb是不同的。現在我們看看 AVDT_ConnectReq的實現:
/******************************************************************************* ** ** Function AVDT_ConnectReq ** ** Description This function initiates an AVDTP signaling connection ** to the peer device. When the connection is completed, an ** AVDT_CONNECT_IND_EVT is sent to the application via its ** control callback function. If the connection attempt fails ** an AVDT_DISCONNECT_IND_EVT is sent. The security mask ** parameter overrides the outgoing security mask set in ** AVDT_Register(). ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB *p_ccb = NULL; UINT16 result = AVDT_SUCCESS; tAVDT_CCB_EVT evt; /* find channel control block for this bd addr; if none, allocate one */ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { /* could not allocate channel control block */ result = AVDT_NO_RESOURCES; } } ... if (result == AVDT_SUCCESS) { /* send event to ccb */ evt.connect.p_cback = p_cback;//bta_av_stream0_cback 具體根據index evt.connect.sec_mask = sec_mask; avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt);//進入avdtp 的狀態機 } return result; }
這里我們發現,其做的事情,先分配了 tAVDT_CCB 結構,然后發送AVDT_CCB_API_CONNECT_REQ_EVT 進入到AVDTP的狀態機中,現在我們看看其狀態機實現:
/******************************************************************************* ** ** Function avdt_ccb_event ** ** Description State machine event handling function for ccb ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data) { tAVDT_CCB_ST_TBL state_table; UINT8 action; int i; /* look up the state table for the current state */ state_table = avdt_ccb_st_tbl[p_ccb->state]; /* set next state */ if (p_ccb->state != state_table[event][AVDT_CCB_NEXT_STATE]) { p_ccb->state = state_table[event][AVDT_CCB_NEXT_STATE]; } /* execute action functions */ for (i = 0; i < AVDT_CCB_ACTIONS; i++) { if ((action = state_table[event][i]) != AVDT_CCB_IGNORE) { (*avdt_cb.p_ccb_act[action])(p_ccb, p_data); } else { break; } } }
發現和我們之前遇到的狀態機實現的套路是完全一致的,都是先查找對應的狀態表,然后設置下一個狀態,最后執行狀態表中該action 對應的函數。
剛開始的時候AVDT CCB的狀態是idle 狀態:avdt_ccb_st_idle :
/* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST},
那么下一個狀態是AVDT_CCB_OPENING_ST,這里需要注意的是,涉及的狀態機非常多,每一個protocol 都可能會涉及到狀態機,不要搞亂。
這里執行的action有兩個:AVDT_CCB_SET_CONN和AVDT_CCB_CHAN_OPEN,我們分別看其實現:
/******************************************************************************* ** ** Function avdt_ccb_set_conn ** ** Description Set CCB variables associated with AVDT_ConnectReq(). ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { /* save callback */ p_ccb->p_conn_cback = p_data->connect.p_cback;//保存回調bta_av_stream0_cback /* set security level */ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_data->connect.sec_mask, AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); }
繼續看channel open 的流程:
/******************************************************************************* ** ** Function avdt_ccb_chan_open ** ** Description This function calls avdt_ad_open_req() to ** initiate a signaling channel connection. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { UNUSED(p_data); BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG); avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT);//打開channel }
我們知道channel 是建立於l2cap,那么打開channel,就會建立l2cap 通道。我們繼續分析:
/******************************************************************************* ** ** Function avdt_ad_open_req ** ** Description This function is called by a CCB or SCB to open a transport ** channel. This function allocates and initializes a ** transport channel table entry. The channel can be opened ** in two roles: as an initiator or acceptor. When opened ** as an initiator the function will start an L2CAP connection. ** When opened as an acceptor the function simply configures ** the table entry to listen for an incoming channel. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role) { tAVDT_TC_TBL *p_tbl; UINT16 lcid; if((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL) { AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl"); return; } p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);//0 if (type == AVDT_CHAN_SIG) { /* if signaling, get mtu from registration control block */ p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; } else { ... } /* if we're acceptor, we're done; just sit back and listen */ if (role == AVDT_ACP) { p_tbl->state = AVDT_AD_ST_ACP; } /* else we're inititator, start the L2CAP connection */ else //打開l2cap的通道 { p_tbl->state = AVDT_AD_ST_CONN; /* call l2cap connect req */ if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 0) { /* if connect req ok, store tcid in lcid table */ avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d", (lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x", avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid); } else { /* if connect req failed, call avdt_ad_tc_close_ind() */ avdt_ad_tc_close_ind(p_tbl, 0); } } }
這里的邏輯其實很簡單,就是基於l2cap 建立AVDTP的通道,在AVDTP register 的時候注冊到l2cap的數組函數是 avdt_l2c_appl:
L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO *) &avdt_l2c_appl);
/* L2CAP callback function structure */ const tL2CAP_APPL_INFO avdt_l2c_appl = { avdt_l2c_connect_ind_cback, avdt_l2c_connect_cfm_cback, NULL, avdt_l2c_config_ind_cback, avdt_l2c_config_cfm_cback, avdt_l2c_disconnect_ind_cback, avdt_l2c_disconnect_cfm_cback, NULL, avdt_l2c_data_ind_cback, avdt_l2c_congestion_ind_cback, NULL /* tL2CA_TX_COMPLETE_CB */ };
l2cap連接之后,對方回復的處理函數應該是avdt_l2c_connect_cfm_cback:,這個函數就不詳細分析了,l2cap channel的連接流程都是先連接,然后配置參數,后續的肯定會執行到L2CA_ConfigReq,我們現在看看配置參數完成之后的流程:
/******************************************************************************* ** ** Function avdt_l2c_config_cfm_cback ** ** Description This is the L2CAP config confirm callback function. ** ** ** Returns void ** *******************************************************************************/ void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) { tAVDT_TC_TBL *p_tbl; /* look up info for this channel */ if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { p_tbl->lcid = lcid; /* if in correct state */ if (p_tbl->state == AVDT_AD_ST_CFG) { /* if result successful */ if (p_cfg->result == L2CAP_CONN_OK) { /* update cfg_flags */ p_tbl->cfg_flags |= AVDT_L2C_CFG_CFM_DONE; /* if configuration complete */ if (p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) { avdt_ad_tc_open_ind(p_tbl);//匯報channel open event } } /* else failure */ else { /* Send L2CAP disconnect req */ L2CA_DisconnectReq(lcid); } } } }
我們繼續分析avdt_ad_tc_open_ind,其主要是同住ccb、scb channel open event,我們看代碼實現:
/******************************************************************************* ** ** Function avdt_ad_tc_open_ind ** ** Description This function is called by the L2CAP interface when ** the L2CAP channel is opened. It looks up the CCB or SCB ** for the channel and sends it an open event. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; tAVDT_OPEN open; tAVDT_EVT_HDR evt; p_tbl->state = AVDT_AD_ST_OPEN; /* if signaling channel, notify ccb that channel open */ if (p_tbl->tcid == 0)//當前是先打開signaling channel 進行配置 { /* set the signal channel to use high priority within the ACL link */ L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid, L2CAP_CHNL_PRIORITY_HIGH); p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); /* use err_param to indicate the role of connection. * AVDT_ACP, if ACP */ evt.err_param = AVDT_INT; if(p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) { evt.err_param = AVDT_ACP; } avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt);//傳給ccb來處理 } /* if media or other channel, notify scb that channel open */ else { /* look up scb in stream routing table by ccb, tcid */ p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); /* put lcid in event data */ if (p_scb != NULL) { open.peer_mtu = p_tbl->peer_mtu; open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid; open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid); avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open);//傳給scb來處理 media channel 信息 } } }
這里發現,控制信號是ccb 來處理的,media 數據 是scb 來處理的。我們繼續看avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt)的處理流程:
傳來的事件是AVDT_CCB_LL_OPEN_EVT:
當前的狀態是AVDT_CCB_OPENING_ST,
/* LL_OPEN_EVT */ {AVDT_CCB_SND_CMD, AVDT_CCB_LL_OPENED, AVDT_CCB_OPEN_ST},
下一個狀態是AVDT_CCB_OPEN_ST , 當前狀態需要執行的動作是AVDT_CCB_SND_CMD和AVDT_CCB_LL_OPENED,這里分析發現前者是去執行隊列中queue住的命令,那這里不去具體分析了,直接看AVDT_CCB_LL_OPENED的流程:
看這個函數的名字,能猜到是已經open 完成,然后做一系列的上報狀態的動作。
/******************************************************************************* ** ** Function avdt_ccb_ll_opened ** ** Description Call callback on open. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_ll_opened(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { tAVDT_CTRL avdt_ctrl; p_ccb->ll_opened = TRUE; if (!p_ccb->p_conn_cback)//!= NULL p_ccb->p_conn_cback = avdt_cb.p_conn_cback;//沒有執行 /* call callback */ if (p_ccb->p_conn_cback) { avdt_ctrl.hdr.err_code = 0; avdt_ctrl.hdr.err_param = p_data->msg.hdr.err_param; (*p_ccb->p_conn_cback)(0, p_ccb->peer_addr, AVDT_CONNECT_IND_EVT, &avdt_ctrl);//bta_av_stream0_cback
} }
參考之前a2dp 初始化的分析發現,這里的回調就是bta_av_stream0_cback,我們繼續分析:
/******************************************************************************* ** ** Function bta_av_stream0_cback ** ** Description This is the AVDTP callback function for stream events. ** ** Returns void ** *******************************************************************************/ static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) { APPL_TRACE_VERBOSE("bta_av_stream0_cback avdt_handle: %d event=0x%x", handle, event); bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0); }
BTA_AV_AVDT_CONNECT_EVT
我們繼續看bta_av_proc_stream_evt的處理:
/******************************************************************************* ** ** Function bta_av_proc_stream_evt ** ** Description Utility function to compose stream events. ** ** Returns void ** *******************************************************************************/ static void bta_av_proc_stream_evt(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data, int index) { tBTA_AV_STR_MSG *p_msg; UINT16 sec_len = 0; tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[index]; int xx; ... if (p_scb && (p_msg = (tBTA_AV_STR_MSG *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_STR_MSG) + sec_len))) != NULL) { /* copy event data, bd addr, and handle to event message buffer */ p_msg->hdr.offset = 0; if (bd_addr != NULL) { bdcpy(p_msg->bd_addr, bd_addr); APPL_TRACE_DEBUG(" bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); } if (p_data != NULL) { memcpy(&p_msg->msg, p_data, sizeof (tAVDT_CTRL)); /* copy config params to event message buffer */ ... } else p_msg->msg.hdr.err_code = 0; /* look up application event */ if ((p_data == NULL) || (p_data->hdr.err_code == 0)) { p_msg->hdr.event = bta_av_stream_evt_ok[event];//從AVDTP的事件轉換到BTA的事件BTA_AV_AVDT_CONNECT_EVT } else { p_msg->hdr.event = bta_av_stream_evt_fail[event]; } p_msg->initiator = FALSE; if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = TRUE; p_msg->hdr.layer_specific = p_scb->hndl; p_msg->handle = handle; p_msg->avdt_event = event; bta_sys_sendmsg(p_msg); }
/* coverity[var_deref_model] */
/* false-positive: bta_av_conn_cback only processes AVDT_CONNECT_IND_EVT and AVDT_DISCONNECT_IND_EVT event
* these 2 events always have associated p_data */
if (p_data)
{
bta_av_conn_cback(handle, bd_addr, event, p_data);
}
else
{
APPL_TRACE_ERROR("%s: p_data is null", __func__);
}
這里做了兩件事:
- 發送BTA_AV_AVDT_CONNECT_EVT 到btu task
- 執行回調bta_av_conn_cback(handle, bd_addr, event, p_data);
BTA_AV_AVDT_CONNECT_EVT根據之前分析,處理該event的狀態機是bta_av_ssm_execute,這個是stream control block,是和stream 相關的。
AV Sevent(0x41)=0x1226(AVDT_CONNECT) state=2(OPENING)
當前的狀態是opening->bta_av_sst_opening,
/* AVDT_CONNECT_EVT */ {BTA_AV_DISCOVER_REQ, BTA_AV_SIGNORE, BTA_AV_OPENING_SST },
這里發現,stream 的下一個狀態還是opening,這里需要執行的行為是BTA_AV_DISCOVER_REQ, 執行的函數是bta_av_discover_req,這里就開始了AVDTP signaling的流程了。在分析這個流程之前,我們先看一下上面回調函數做了什么?
void bta_av_conn_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) { tBTA_AV_STR_MSG *p_msg; UINT16 evt = 0; if (event == BTA_AR_AVDT_CONN_EVT || event == AVDT_CONNECT_IND_EVT || event == AVDT_DISCONNECT_IND_EVT) { evt = BTA_AV_SIG_CHG_EVT; if(AVDT_DISCONNECT_IND_EVT == event) p_scb = bta_av_addr_to_scb(bd_addr); if ((p_msg = (tBTA_AV_STR_MSG *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_STR_MSG)))) != NULL) { p_msg->hdr.event = evt;//發送BTA_AV_SIG_CHG_EVT p_msg->hdr.layer_specific = event;//AVDT_CONNECT_IND_EVT = 16 p_msg->hdr.offset = p_data->hdr.err_param; bdcpy(p_msg->bd_addr, bd_addr); bta_sys_sendmsg(p_msg); } } }
該事件 最終會執行:bta_av_sig_chg主要是在bta_av_cb中分配一個和對端地址相對應的tBTA_AV_LCB結構. 對於bta stream state machine沒有影響,略過。
下面我們重點分析AVDTP signaling的流程,bta_av_discover_req:這個流程,在hci log也是有所體現,
大概就是分為discover、get capability、set configuration以及open
下面我們逐步分析這個代碼流程:
/******************************************************************************* ** ** Function bta_av_discover_req ** ** Description Send an AVDTP discover request to the peer. ** ** Returns void ** *******************************************************************************/ void bta_av_discover_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { UNUSED(p_data); /* send avdtp discover request */ AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS, bta_av_dt_cback[p_scb->hdi]);//即將進入到AVDTP 的狀態機 }
搜索的結果會保存到p_Scb_sep_info,下面的代碼分析會非常的繁瑣,這里分析的意義是為了搞清楚AVDTP是如何用代碼的形式呈現出來的。
/******************************************************************************* ** ** Function AVDT_DiscoverReq ** ** Description This function initiates a connection to the AVDTP service ** on the peer device, if not already present, and discovers ** the stream endpoints on the peer device. (Please note ** that AVDTP discovery is unrelated to SDP discovery). ** This function can be called at any time regardless of whether ** there is an AVDTP connection to the peer device. ** ** When discovery is complete, an AVDT_DISCOVER_CFM_EVT ** is sent to the application via its callback function. ** The application must not call AVDT_GetCapReq() or ** AVDT_DiscoverReq() again to the same device until ** discovery is complete. ** ** The memory addressed by sep_info is allocated by the ** application. This memory is written to by AVDTP as part ** of the discovery procedure. This memory must remain ** accessible until the application receives the ** AVDT_DISCOVER_CFM_EVT. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB *p_ccb; UINT16 result = AVDT_SUCCESS; tAVDT_CCB_EVT evt; /* find channel control block for this bd addr; if none, allocate one */ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { ... } if (result == AVDT_SUCCESS) { ... /* send event to ccb */ else { evt.discover.p_sep_info = p_sep_info; evt.discover.num_seps = max_seps; evt.discover.p_cback = p_cback; avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt);//進入AVDTP狀態機 } } return result; }
我們繼續看AVDTP狀態機的運轉:
/******************************************************************************* ** ** Function avdt_ccb_event ** ** Description State machine event handling function for ccb ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data) { tAVDT_CCB_ST_TBL state_table; UINT8 action; int i; state_table = avdt_ccb_st_tbl[p_ccb->state]; /* set next state */ if (p_ccb->state != state_table[event][AVDT_CCB_NEXT_STATE]) { p_ccb->state = state_table[event][AVDT_CCB_NEXT_STATE]; } /* execute action functions */ for (i = 0; i < AVDT_CCB_ACTIONS; i++) { if ((action = state_table[event][i]) != AVDT_CCB_IGNORE) { (*avdt_cb.p_ccb_act[action])(p_ccb, p_data); } else { break; } } }
根據前面的分析,我們已經知道,當前的AVDTP 的channel 的狀態已經是打開狀態了:AVDT_CCB_OPEN_ST
/* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST},
下一個狀態依然是 AVDT_CCB_OPEN_ST,執行的action是AVDT_CCB_SND_DISCOVER_CMD和AVDT_CCB_SND_CMD,
我們來分析一下AVDT_CCB_SND_DISCOVER_CMD,
/******************************************************************************* ** ** Function avdt_ccb_snd_discover_cmd ** ** Description This function is called to send a discover command to the ** peer. It copies variables needed for the procedure from ** the event to the CCB. It marks the CCB as busy and then ** sends a discover command. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_snd_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { /* store info in ccb struct */ p_ccb->p_proc_data = p_data->discover.p_sep_info; p_ccb->proc_cback = p_data->discover.p_cback; p_ccb->proc_param = p_data->discover.num_seps; /* we're busy */ p_ccb->proc_busy = TRUE;//標志當前狀態為busy /* build and queue discover req */ avdt_msg_send_cmd(p_ccb, NULL, AVDT_SIG_DISCOVER, NULL); }
這個函數就是 向對方的設備發送一個discovery的請求,對應的hci log中:Signaling Identifier: AVDTP_DISCOVER
這里因為篇幅的原因就不往下 再分析avdt_msg_send_cmd的流程了。
那當對方回應我們的這條數據的時候,會調用到哪個函數呢?這個其實上面已經分析了L2CA_Register這個函數,(當然L2cap 的狀態機輪轉,我們這里就不分析了),注冊的是avdt_l2c_appl,
執行的函數是avdt_l2c_data_ind_cback,這里也是一步步路由,從l2cap 上傳上來的event 是AVDTP_CCB_MSG_DISCOVER_RSP_EVT,到 channel control block:
/* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_OPEN_ST},
然后執行action:AVDT_CCB_HDL_DISCOVER_RSP,下一個狀態依然不變,都是打開狀態,我們繼續看其實現:avdt_ccb_hdl_discover_rsp:
/******************************************************************************* ** ** Function avdt_ccb_hdl_discover_rsp ** ** Description This function is called when a discover response or ** reject is received from the peer. It calls the application ** callback function with the results. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { /* we're done with procedure */ p_ccb->proc_busy = FALSE; /* call app callback with results */ (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT, (tAVDT_CTRL *)(&p_data->msg.discover_rsp)); }
p_ccb->proc_cback其實就是之前 進行AVDT_DiscoverReq傳入的參數bta_av_stream0_cback(這里假定就是bta_av_stream0_cback),
/******************************************************************************* ** ** Function bta_av_stream0_cback ** ** Description This is the AVDTP callback function for stream events. ** ** Returns void ** *******************************************************************************/ static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) { APPL_TRACE_VERBOSE("bta_av_stream0_cback avdt_handle: %d event=0x%x", handle, event); bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0); }
發現執行的真正的函數是bta_av_proc_stream_evt,並且最后一個參數是 index,這樣封裝有什么好處呢?我能想到的是 使得代碼的層次感很強,可讀性很好。
我們繼續分析:
/* look up application event */ if ((p_data == NULL) || (p_data->hdr.err_code == 0)) { p_msg->hdr.event = bta_av_stream_evt_ok[event];//將AVDTP的event 轉換成BTA的event:AVDT_DISCOVER_CFM_EVT-->BTA_AV_STR_DISC_OK_EVT } else { p_msg->hdr.event = bta_av_stream_evt_fail[event]; } p_msg->initiator = FALSE; if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = TRUE; APPL_TRACE_VERBOSE("hndl:x%x", p_scb->hndl); p_msg->hdr.layer_specific = p_scb->hndl; p_msg->handle = handle; p_msg->avdt_event = event; bta_sys_sendmsg(p_msg);//發送mesg } /* coverity[var_deref_model] */ /* false-positive: bta_av_conn_cback only processes AVDT_CONNECT_IND_EVT and AVDT_DISCONNECT_IND_EVT event * these 2 events always have associated p_data */ if (p_data) { bta_av_conn_cback(handle, bd_addr, event, p_data); } else { APPL_TRACE_ERROR("%s: p_data is null", __func__); } }
發送的event 是 AVDT_DISCOVER_CFM_EVT,這里發現最后的回調bta_av_conn_cback是不處理 這個event 的,我們直接看看發送msg 到btu task的情況。
BTA_AV_STR_DISC_OK_EVT,//0x1216
BTA got event 0x1216
發送到btu 的event ,最終路由到bta_av_hdl_event 來統一處理,這里根據不同的event ,也是路由到不同的處理函數,當前的0x1216 是通過bta_av_ssm_execute 來處理:
當前的狀態是bta_av_sst_opening,
/* STR_DISC_OK_EVT */ {BTA_AV_DISC_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST },
下一個狀態依然是BTA_AV_OPENING_SST,執行的action 是BTA_AV_DISC_RESULTS,那么看來是要對discovery 的結果進行處理,
/******************************************************************************* ** ** Function bta_av_disc_results ** ** Description Handle the AVDTP discover results. Search through the ** results and find the first available stream, and get ** its capabilities. ** ** Returns void ** *******************************************************************************/ void bta_av_disc_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { UINT8 num_snks = 0, num_srcs =0, i; /* our uuid in case we initiate connection */ UINT16 uuid_int = p_scb->uuid_int; APPL_TRACE_DEBUG(" initiator UUID 0x%x", uuid_int); /* store number of stream endpoints returned */ p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps; for (i = 0; i < p_scb->num_seps; i++) { /* steam not in use, is a sink, and is audio */ if ((p_scb->sep_info[i].in_use == FALSE) && (p_scb->sep_info[i].media_type == p_scb->media_type)) { if((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) && (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)) num_snks++; if((p_scb->sep_info[i].tsep == AVDT_TSEP_SRC) && (uuid_int == UUID_SERVCLASS_AUDIO_SINK)) num_srcs++; } } p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, num_srcs, p_scb->peer_addr,uuid_int);//bta_av_co_audio_disc_res p_scb->num_disc_snks = num_snks; p_scb->num_disc_srcs = num_srcs; /* if we got any */ if (p_scb->num_seps > 0) { /* initialize index into discovery results */ p_scb->sep_info_idx = 0; /* get the capabilities of the first available stream */ bta_av_next_getcap(p_scb, p_data);//開始了獲取各個端子的能力 } /* else we got discover response but with no streams; we're done */ else { bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, p_data); } }
從這里,我們意識到 代碼的流程在處理 discovery 的resp的時候,當拿到對端 端子的數據的時候,就開始 對各個端子進行 獲取能力。
下面我們看bta_av_next_getcap的流程:其對應到hci 的log,如下:
我們 進一步分析:
/******************************************************************************* ** ** Function bta_av_next_getcap ** ** Description The function gets the capabilities of the next available ** stream found in the discovery results. ** ** Returns TRUE if we sent request to AVDT, FALSE otherwise. ** *******************************************************************************/ static BOOLEAN bta_av_next_getcap(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { int i; tAVDT_GETCAP_REQ *p_req; BOOLEAN sent_cmd = FALSE; UINT16 uuid_int = p_scb->uuid_int; UINT8 sep_requested = 0; if(uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) sep_requested = AVDT_TSEP_SNK; else if(uuid_int == UUID_SERVCLASS_AUDIO_SINK) sep_requested = AVDT_TSEP_SRC; for (i = p_scb->sep_info_idx; i < p_scb->num_seps; i++) { /* steam not in use, is a sink, and is the right media type (audio/video) */ if ((p_scb->sep_info[i].in_use == FALSE) && (p_scb->sep_info[i].tsep == sep_requested) && (p_scb->sep_info[i].media_type == p_scb->media_type)) { p_scb->sep_info_idx = i; /* we got a stream; get its capabilities */ if (p_scb->p_cap == NULL) { p_scb->p_cap = (tAVDT_CFG *) GKI_getbuf(sizeof(tAVDT_CFG)); } if (p_scb->p_cap == NULL) { i = p_scb->num_seps; break; } if (p_scb->avdt_version >= AVDT_VERSION_SYNC) { p_req = AVDT_GetAllCapReq;//獲取cap的函數 } else { p_req = AVDT_GetCapReq; } (*p_req)(p_scb->peer_addr, p_scb->sep_info[i].seid, p_scb->p_cap, bta_av_dt_cback[p_scb->hdi]);//開始獲取cap sent_cmd = TRUE; break; } } ... return sent_cmd; }
這里注意獲取cap的最后一個參數和 discovery 的最后一個參數一樣,其本質都是bta_av_stream0_cback回調函數。我們繼續分析:
/******************************************************************************* ** ** Function AVDT_GetAllCapReq ** ** Description This function initiates a connection to the AVDTP service ** on the peer device, if not already present, and gets the ** capabilities of a stream endpoint on the peer device. ** This function can be called at any time regardless of ** whether there is an AVDTP connection to the peer device. ** ** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is ** sent to the application via its callback function. The ** application must not call AVDT_GetCapReq() or ** AVDT_DiscoverReq() again until the procedure is complete. ** ** The memory pointed to by p_cfg is allocated by the ** application. This memory is written to by AVDTP as part ** of the get capabilities procedure. This memory must ** remain accessible until the application receives ** the AVDT_GETCAP_CFM_EVT. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback) { tAVDT_CCB_API_GETCAP getcap; getcap.single.seid = seid; getcap.single.sig_id = AVDT_SIG_GET_ALLCAP; getcap.p_cfg = p_cfg; getcap.p_cback = p_cback; return avdt_get_cap_req (bd_addr, &getcap); }
這里函數的注釋寫的非常清楚,就不多加介紹了。最終會執行avdt_ccb_event 進入到AVDTP的狀態機中,當函數返回的時候,這部分的流程和discovery的流程非常的相似,只是執行的具體的函數不一樣,當get cap的函數返回的時候,會有
MSG_GETCAP_RSP_EVT 的返回,
/* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_OPEN_ST},
這里討論都類似,我們看看 AVDT_CCB_HDL_GETCAP_RSP 執行的動作:avdt_ccb_hdl_getcap_rsp
/******************************************************************************* ** ** Function avdt_ccb_hdl_getcap_rsp ** ** Description This function is called with a get capabilities response ** or reject is received from the peer. It calls the ** application callback function with the results. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { /* we're done with procedure */ p_ccb->proc_busy = FALSE; /* call app callback with results */ (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT, (tAVDT_CTRL *)(&p_data->msg.svccap));//和discovery一樣,執行回調bta_av_stream0_cback }
我們繼續看:
想想discovery 的流程是如何處理的?這里也是類似的,
/* look up application event */ if ((p_data == NULL) || (p_data->hdr.err_code == 0)) { p_msg->hdr.event = bta_av_stream_evt_ok[event];//BTA_AV_STR_GETCAP_OK_EVT } else { p_msg->hdr.event = bta_av_stream_evt_fail[event]; } p_msg->initiator = FALSE; if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = TRUE; APPL_TRACE_VERBOSE("hndl:x%x", p_scb->hndl); p_msg->hdr.layer_specific = p_scb->hndl; p_msg->handle = handle; p_msg->avdt_event = event; bta_sys_sendmsg(p_msg);
看看對於這個事件的處理,肯定也是處理get cap的results:
/* STR_GETCAP_OK_EVT */ {BTA_AV_GETCAP_RESULTS, BTA_AV_SIGNORE, BTA_AV_OPENING_SST },
我們看看 對於這個event的處理:
從上面的hci log,我們應該可以猜測到,這個函數里面處理了 get cap的結果之后,肯定是要進行set configuration 的動作:
/******************************************************************************* ** ** Function bta_av_getcap_results ** ** Description Handle the AVDTP get capabilities results. Check the codec ** type and see if it matches ours. If it does not, get the ** capabilities of the next stream, if any. ** ** Returns void ** *******************************************************************************/ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { tAVDT_CFG cfg; UINT8 media_type; tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx]; UINT16 uuid_int; /* UUID for which connection was initiatied */ memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG)); cfg.num_codec = 1; cfg.num_protect = p_scb->p_cap->num_protect; memcpy(cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE); memcpy(cfg.protect_info, p_scb->p_cap->protect_info, AVDT_PROTECT_SIZE); media_type = p_scb->p_cap->codec_info[BTA_AV_MEDIA_TYPE_IDX] >> 4; /* if codec present and we get a codec configuration */ if ((p_scb->p_cap->num_codec != 0) && (media_type == p_scb->media_type) && (p_scb->p_cos->getcfg(p_scb->hndl, p_scb->p_cap->codec_info[BTA_AV_CODEC_TYPE_IDX], cfg.codec_info, &p_scb->sep_info_idx, p_info->seid, &cfg.num_protect, cfg.protect_info) == 0)) { #if AVDT_MULTIPLEXING == TRUE cfg.mux_mask &= p_scb->p_cap->mux_mask; #endif /* save copy of codec type and configuration */ p_scb->codec_type = cfg.codec_info[BTA_AV_CODEC_TYPE_IDX]; memcpy(&p_scb->cfg, &cfg, sizeof(tAVDT_CFG)); uuid_int = p_scb->uuid_int; if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC)); else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK)); /* use only the services peer supports */ cfg.psc_mask &= p_scb->p_cap->psc_mask; p_scb->cur_psc_mask = cfg.psc_mask; ... /* open the stream */ AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg);//打開請求 if (!bta_av_is_rcfg_sst(p_scb)) { /* free capabilities buffer */ utl_freebuf((void **) &p_scb->p_cap); } } else { /* try the next stream, if any */ p_scb->sep_info_idx++; bta_av_next_getcap(p_scb, p_data); } }
我們發現上面代碼流程直接走了 AVDTP open的流程,為什么沒有去set configuration,我們繼續看:
/******************************************************************************* ** ** Function AVDT_OpenReq ** ** Description This function initiates a connection to the AVDTP service ** on the peer device, if not already present, and connects ** to a stream endpoint on a peer device. When the connection ** is completed, an AVDT_OPEN_CFM_EVT is sent to the ** application via the control callback function for this handle. ** ** Returns AVDT_SUCCESS if successful, otherwise error. ** *******************************************************************************/ UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg) { tAVDT_CCB *p_ccb = NULL; tAVDT_SCB *p_scb = NULL; UINT16 result = AVDT_SUCCESS; tAVDT_SCB_EVT evt; /* verify SEID */ if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) { result = AVDT_BAD_PARAMS; } /* map handle to scb */ else if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) { result = AVDT_BAD_HANDLE; } /* find channel control block for this bd addr; if none, allocate one */ else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) { if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) { /* could not allocate channel control block */ result = AVDT_NO_RESOURCES; } } /* send event to scb */ if (result == AVDT_SUCCESS) { evt.msg.config_cmd.hdr.seid = seid; evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); evt.msg.config_cmd.int_seid = handle; evt.msg.config_cmd.p_cfg = p_cfg; avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt);//進行set configuration } return result; }
我們發現是在open 里面實現的:,這里需要注意,我們前面discovery和get cap 都是 發送給ccb,但是這里開始發送給scb,此時的scb還是idle的狀態,
/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_SND_SETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST},
那么scb 的下一個狀態依然是idle 狀態。 這里執行的action 是AVDT_SCB_SND_SETCONFIG_REQ,我們具體看看:
這里執行的函數 是avdt_scb_snd_setconfig_req:我們繼續分析下這個函數:
/******************************************************************************* ** ** Function avdt_scb_snd_setconfig_req ** ** Description This function marks the SCB as in use and copies the ** configuration parameters to the SCB. Then the function ** sends a set configuration command message and initiates ** opening of the signaling channel. ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { tAVDT_CFG *p_req, *p_cfg; /* copy API parameters to scb, set scb as in use */ p_scb->in_use = TRUE; p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid; p_req = p_data->msg.config_cmd.p_cfg; p_cfg = &p_scb->cs.cfg; #if AVDT_MULTIPLEXING == TRUE p_req->mux_tsid_media = p_cfg->mux_tsid_media; p_req->mux_tcid_media = p_cfg->mux_tcid_media; if(p_req->psc_mask & AVDT_PSC_REPORT) { p_req->mux_tsid_report = p_cfg->mux_tsid_report; p_req->mux_tcid_report = p_cfg->mux_tcid_report; } #endif memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG)); avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg);//發送set config 的消息 /* tell ccb to open channel */ avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL);//什么也沒做 }
分析到avdt_msg_send_cmd,我們這里就不往下繼續分析了,后續對端收到set configuration的消息之后會回復我們。
現在我們看下avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL);做的事情,按照之前的邏輯,stream channel open的操作應該是在set configuration rsp 里面執行,我們看看是不是這樣:
/* LL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST},
我們發現,其action 都是ignore,所以什么也沒干,狀態還是AVDT_CCB_OPEN_ST
下面我們繼續分析,set configuration 之后的rsp 的處理:
/******************************************************************************* ** ** Function avdt_l2c_data_ind_cback ** ** Description This is the L2CAP data indication callback function. ** ** ** Returns void ** *******************************************************************************/ void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) { tAVDT_TC_TBL *p_tbl; /* look up info for this channel */ if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { avdt_ad_tc_data_ind(p_tbl, p_buf); } else /* prevent buffer leak */ GKI_freebuf(p_buf); }
找到對應的channel:
/******************************************************************************* ** ** Function avdt_ad_tc_data_ind ** ** Description This function is called by the L2CAP interface layer when ** incoming data is received from L2CAP. It looks up the CCB ** or SCB for the channel and routes the data accordingly. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; /* store type (media, recovery, reporting) */ p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid); /* if signaling channel, handle control message */ if (p_tbl->tcid == 0) { p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); avdt_msg_ind(p_ccb, p_buf);//signal channel } /* if media or other channel, send event to scb */ else { p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); if (p_scb != NULL) { avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT *) &p_buf); } else { GKI_freebuf(p_buf); AVDT_TRACE_ERROR(" avdt_ad_tc_data_ind buffer freed"); } } }
我們當前是signal 信號:執行avdt_msg_ind 函數:
/******************************************************************************* ** ** Function avdt_msg_ind ** ** Description This function is called by the adaption layer when an ** incoming message is received on the signaling channel. ** It parses the message and sends an event to the appropriate ** SCB or CCB for the message. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf) { tAVDT_SCB *p_scb; ... if (ok && !gen_rej) { /* skip over header (msg length already verified during reassembly) */ p_buf->len -= AVDT_LEN_TYPE_SINGLE; /* set up to parse message */ if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_DISCOVER)) { /* parse discover rsp message to struct supplied by app */ msg.discover_rsp.p_sep_info = (tAVDT_SEP_INFO *) p_ccb->p_proc_data; msg.discover_rsp.num_seps = p_ccb->proc_param; } else if ((msg_type == AVDT_MSG_TYPE_RSP) && ((sig == AVDT_SIG_GETCAP) || (sig == AVDT_SIG_GET_ALLCAP))) { /* parse discover rsp message to struct supplied by app */ msg.svccap.p_cfg = (tAVDT_CFG *) p_ccb->p_proc_data; } else if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_GETCONFIG)) { /* parse get config rsp message to struct allocated locally */ msg.svccap.p_cfg = &cfg; } else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_SETCONFIG)) { /* parse config cmd message to struct allocated locally */ msg.config_cmd.p_cfg = &cfg; } ... /* parse message; while we're at it map message sig to event */ if (msg_type == AVDT_MSG_TYPE_CMD) { msg.hdr.err_code = err = (*avdt_msg_prs_cmd[sig - 1])(&msg, p, p_buf->len); evt = avdt_msg_cmd_2_evt[sig - 1]; } else if (msg_type == AVDT_MSG_TYPE_RSP) { msg.hdr.err_code = err = (*avdt_msg_prs_rsp[sig - 1])(&msg, p, p_buf->len); evt = avdt_msg_rsp_2_evt[sig - 1];//找到對應的event } else /* msg_type == AVDT_MSG_TYPE_REJ */ { err = avdt_msg_prs_rej(&msg, p, sig); evt = avdt_msg_rej_2_evt[sig - 1]; } ... if (ok) { /* if it's a ccb event send to ccb */ if (evt & AVDT_CCB_MKR) { avdt_ccb_event(p_ccb, (UINT8)(evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg);//進入到avdt_ccb進行處理,這里處理的event:(AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR)+(AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR),也就是avdtp signal的前兩個流程 } /* if it's a scb event */ else { /* Scb events always have a single seid. For cmd, get seid from ** message. For rej and rsp, get seid from p_curr_cmd. */ if (msg_type == AVDT_MSG_TYPE_CMD) { scb_hdl = msg.single.seid; } else { scb_hdl = *((UINT8 *)(p_ccb->p_curr_cmd + 1)); } /* Map seid to the scb and send it the event. For cmd, seid has ** already been verified by parsing function. */ if (evt && (p_scb = avdt_scb_by_hdl(scb_hdl)) != NULL) { avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg);//AVDT_SCB_MSG_SETCONFIG_RSP_EVT 應該是scb的event } } } /* free message buffer */ GKI_freebuf(p_buf); /* if its a rsp or rej, send event to ccb to free associated ** cmd msg buffer and handle cmd queue */ if (handle_rsp) { avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL);//最后發送AVDT_CCB_RCVRSP_EVT到ccb }
我們分別看avdt_scb_event(p_scb,AVDT_SCB_MSG_SETCONFIG_RSP_EVT , (tAVDT_SCB_EVT *) &msg)和avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL);,后者沒有做影響流程的操作,執行的action是AVDT_CCB_FREE_CMD和AVDT_CCB_SND_CMD,這里不詳細分析了.
01-15 08:38:14.215 I/bt_avp ( 4239): SCB hdl=1 event=29/MSG_SETCONFIG_RSP_EVT state=SCB_IDLE_ST 01-15 08:38:14.215 I/bt_avp ( 4239): SCB hdl=1 event=5/API_OPEN_REQ_EVT state=SCB_CONF_ST 01-15 08:38:14.216 I/bt_avp ( 4239): CCB ccb=0 event=SENDMSG_EVT state=CCB_OPEN_ST 01-15 08:38:14.216 I/bt_avp ( 4239): CCB ccb=0 event=RCVRSP_EVT state=CCB_OPEN_ST
上面是次流程打印的log:
/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_SETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST},
scb的下一個狀態是AVDT_SCB_CONF_ST
執行的action 是AVDT_SCB_HDL_SETCONFIG_RSP,我們看看 該函數做了什么?按照預期應該會去保存config的結果以及 執行 scb open的操作:
/******************************************************************************* ** ** Function avdt_scb_hdl_setconfig_rsp ** ** Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT ** to initiate sending of an open command message. ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { tAVDT_EVT_HDR single; UNUSED(p_data); if (p_scb->p_ccb != NULL) { /* save configuration */ memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); /* initiate open */ single.seid = p_scb->peer_seid; avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single);//open } }
符合我們的預期,下一步進入到 對於scb 的open 的操作中:
我們繼續看:
/* API_OPEN_REQ_EVT */ {AVDT_SCB_SND_OPEN_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST},
scb的下一個狀態 依然是AVDT_SCB_CONF_ST,執行的action 是AVDT_SCB_SND_OPEN_REQ,:
/******************************************************************************* ** ** Function avdt_scb_snd_open_req ** ** Description This function sends an open command message. ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { tAVDT_EVT_HDR hdr; UNUSED(p_data); hdr.seid = p_scb->peer_seid; avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr);//向對方發出AVDT_SIG_OPEN }
開始 向對端發送 scb open的請求,我們現在分析一下,對方的response:
此時對方傳過來的event是AVDT_SCB_MSG_OPEN_RSP_EVT,
/******************************************************************************* ** ** Function avdt_msg_ind ** ** Description This function is called by the adaption layer when an ** incoming message is received on the signaling channel. ** It parses the message and sends an event to the appropriate ** SCB or CCB for the message. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf) { ... if (ok) { /* if it's a ccb event send to ccb */ if (evt & AVDT_CCB_MKR) { avdt_ccb_event(p_ccb, (UINT8)(evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); } /* if it's a scb event */ else { /* Scb events always have a single seid. For cmd, get seid from ** message. For rej and rsp, get seid from p_curr_cmd. */ if (msg_type == AVDT_MSG_TYPE_CMD) { scb_hdl = msg.single.seid; } else { scb_hdl = *((UINT8 *)(p_ccb->p_curr_cmd + 1)); } /* Map seid to the scb and send it the event. For cmd, seid has ** already been verified by parsing function. */ if (evt && (p_scb = avdt_scb_by_hdl(scb_hdl)) != NULL) { avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg);//AVDT_SCB_MSG_OPEN_RSP_EVT } } } /* free message buffer */ GKI_freebuf(p_buf); /* if its a rsp or rej, send event to ccb to free associated ** cmd msg buffer and handle cmd queue */ if (handle_rsp) { avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL); }
上面的重點 就是avdt_scb_event(p_scb,AVDT_SCB_MSG_OPEN_RSP_EVT, (tAVDT_SCB_EVT *) &msg);
/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_HDL_OPEN_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST},
現在發現,scb的下一個狀態發生了變化,AVDT_SCB_OPENING_ST,而執行的動作也是處理這個resp:AVDT_SCB_HDL_OPEN_RSP:
/******************************************************************************* ** ** Function avdt_scb_hdl_open_rsp ** ** Description This function calls avdt_ad_open_req() to initiate ** connection of the transport channel for this stream. ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { UNUSED(p_data); /* initiate opening of trans channels for this SEID */ p_scb->role = AVDT_OPEN_INT; avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT); //這里開啟media channel /* start tc connect timer */ btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); }
我們發現 開始 media channel的相關的工作,繼續看:
/******************************************************************************* ** ** Function avdt_ad_open_req ** ** Description This function is called by a CCB or SCB to open a transport ** channel. This function allocates and initializes a ** transport channel table entry. The channel can be opened ** in two roles: as an initiator or acceptor. When opened ** as an initiator the function will start an L2CAP connection. ** When opened as an acceptor the function simply configures ** the table entry to listen for an incoming channel. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role) { tAVDT_TC_TBL *p_tbl; UINT16 lcid; if((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL) { AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl"); return; } p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb); AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d", type, role, p_tbl->tcid); if (type == AVDT_CHAN_SIG) { /* if signaling, get mtu from registration control block */ p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; } else //當前是media channel { /* otherwise get mtu from scb */ p_tbl->my_mtu = p_scb->cs.mtu; p_tbl->my_flush_to = p_scb->cs.flush_to; /* also set scb_hdl in rt_tbl */ avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl = avdt_scb_to_hdl(p_scb); AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].scb_hdl = %d", avdt_ccb_to_idx(p_ccb), p_tbl->tcid, avdt_scb_to_hdl(p_scb)); } /* if we're acceptor, we're done; just sit back and listen */ if (role == AVDT_ACP) { p_tbl->state = AVDT_AD_ST_ACP; } /* else we're inititator, start the L2CAP connection */ else //we are inititator { p_tbl->state = AVDT_AD_ST_CONN; /* call l2cap connect req */ if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 0)//建立l2cap 連接 { /* if connect req ok, store tcid in lcid table */ avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d", (lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x", avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid); } else { /* if connect req failed, call avdt_ad_tc_close_ind() */ avdt_ad_tc_close_ind(p_tbl, 0); } } }
avdtp signaling 的流程走完了,最后是建立media channel.建立l2cap的通道.
在hci log中,也能找到該流程的痕跡:
到這里,avdtp的流程似乎已經講完了,因為基本的流程都已經走完了,但是這里要注意一下,我們上面的scb 狀態機的狀態還是opening 的狀態,BTA層的Stream state machine也是處於opening 的狀態,而btif_AV的狀態機也還處於opening狀態.那什么時候會變成open 狀態,我們需要繼續分析:
上面 建立l2cap的media channel之后(其實也就是stream channel)之后.會進行config,config的response 的處理函數是:avdt_l2c_config_cfm_cback:
/******************************************************************************* ** ** Function avdt_l2c_config_cfm_cback ** ** Description This is the L2CAP config confirm callback function. ** ** ** Returns void ** *******************************************************************************/ void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) { tAVDT_TC_TBL *p_tbl; /* look up info for this channel */ if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) { p_tbl->lcid = lcid; /* if in correct state */ if (p_tbl->state == AVDT_AD_ST_CFG) { /* if result successful */ if (p_cfg->result == L2CAP_CONN_OK) { /* update cfg_flags */ p_tbl->cfg_flags |= AVDT_L2C_CFG_CFM_DONE; /* if configuration complete */ if (p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) { avdt_ad_tc_open_ind(p_tbl);//上報event } } /* else failure */ else { /* Send L2CAP disconnect req */ L2CA_DisconnectReq(lcid); } } } }
我們繼續看avdt_ad_tc_open_ind的流程:,這個函數之前已經提到過,里面有signal 信道和數據通道的不同處理 ,當前的新建的l2cap channel是media channel
/******************************************************************************* ** ** Function avdt_ad_tc_open_ind ** ** Description This function is called by the L2CAP interface when ** the L2CAP channel is opened. It looks up the CCB or SCB ** for the channel and sends it an open event. ** ** ** Returns Nothing. ** *******************************************************************************/ void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl) { tAVDT_CCB *p_ccb; tAVDT_SCB *p_scb; tAVDT_OPEN open; tAVDT_EVT_HDR evt; p_tbl->state = AVDT_AD_ST_OPEN; /* if signaling channel, notify ccb that channel open */ if (p_tbl->tcid == 0) { /* set the signal channel to use high priority within the ACL link */ L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid, L2CAP_CHNL_PRIORITY_HIGH); p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); /* use err_param to indicate the role of connection. * AVDT_ACP, if ACP */ evt.err_param = AVDT_INT; if(p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) { evt.err_param = AVDT_ACP; } avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt);//signal 走的是avdt_ccb_event } /* if media or other channel, notify scb that channel open */ else { /* look up scb in stream routing table by ccb, tcid */ p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); /* put lcid in event data */ if (p_scb != NULL) { open.peer_mtu = p_tbl->peer_mtu; open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid; open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid); avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open);//media 走的是avdt_scb_event } } }
我們發現 去最終執行了avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open);當前的scb 的狀態是opening,我們看看 會發生什么?
/* TC_OPEN_EVT */ {AVDT_SCB_HDL_TC_OPEN, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST},
我們發現,到此,scb的狀態變成了AVDT_SCB_OPEN_ST,我們解開了第一個謎團,下面還有兩個,我們繼續看看.可以猜想一下, 肯定是將open的信息一層一層往上傳,然后逐個變成open狀態的.我們繼續看.
scb 變成open狀態之后,還要 執行action是AVDT_SCB_HDL_TC_OPEN:
/******************************************************************************* ** ** Function avdt_scb_hdl_tc_open ** ** Description This function is called when the transport channel is ** opened while in the opening state. It calls the ** application callback with an open indication or open ** confirm depending on who initiated the open procedure. ** ** Returns Nothing. ** *******************************************************************************/ void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { UINT8 event; #if AVDT_REPORTING == TRUE UINT8 role; #endif /* stop transport channel connect timer */ btu_stop_timer(&p_scb->timer_entry); event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT;//AVDT_OPEN_CFM_EVT p_data->open.hdr.err_code = 0; ... /* call app callback */ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, event, (tAVDT_CTRL *) &p_data->open);//bta_av_stream0_cback:AVDT_OPEN_CFM_EVT }
上面函數的 注釋寫的非常的清楚,就是上班open event.上面的回調函數就是 bta_av_stream0_cback,我們繼續看看回調函數如何處理的:
static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data) { bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0); }
/******************************************************************************* ** ** Function bta_av_proc_stream_evt ** ** Description Utility function to compose stream events. ** ** Returns void ** *******************************************************************************/ static void bta_av_proc_stream_evt(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data, int index) { ... /* look up application event */ if ((p_data == NULL) || (p_data->hdr.err_code == 0)) { p_msg->hdr.event = bta_av_stream_evt_ok[event];//AVDT_OPEN_CFM_EVT-->BTA_AV_STR_OPEN_OK_EVT } else { p_msg->hdr.event = bta_av_stream_evt_fail[event]; } p_msg->initiator = FALSE; if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = TRUE; APPL_TRACE_VERBOSE("hndl:x%x event = 0x%x", p_scb->hndl,event); p_msg->hdr.layer_specific = p_scb->hndl; p_msg->handle = handle; p_msg->avdt_event = event; bta_sys_sendmsg(p_msg); }
...
我們可以看到.這個回調就是將底層傳過來的event轉換成BTA層的event,這里被轉換成(AVDT_OPEN_CFM_EVT--->BTA_AV_STR_OPEN_OK_EVT) ,然后 通過bta_sys_sendmsg發送到btu task,而處理這些event 的狀態機 正是我們前面提到的BTA ssm ,我們繼續看:
通過a2dp 初始化的章節分析,我們知道該event 是由bta_av_ssm_execute 來處理的,此刻的bta ssm 也是處於opening 狀態,
/* STR_OPEN_OK_EVT */ {BTA_AV_ST_RC_TIMER, BTA_AV_STR_OPENED, BTA_AV_OPEN_SST },
這里發現.bta ssm 的狀態也變成了BTA_AV_OPEN_SST狀態,那么我們的第二個謎團解開,繼續看.接下來 他要執行的兩個action是BTA_AV_ST_RC_TIMER BTA_AV_STR_OPENED,從名字上面看看,第二個action應該做了一些上報的事情.
這邊追了一下代碼發現,前者是做了AVRCP的相關的操作,這里暫時不分析.我們看看BTA_AV_STR_OPENED 做的事情:
/******************************************************************************* ** ** Function bta_av_str_opened ** ** Description Stream opened OK (incoming/outgoing). ** ** Returns void ** *******************************************************************************/ void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data) { ... { /* TODO check if other audio channel is open. * If yes, check if reconfig is needed * Rigt now we do not do this kind of checking. * BTA-AV is INT for 2nd audio connection. * The application needs to make sure the current codec_info is proper. * If one audio connection is open and another SNK attempts to connect to AV, * the connection will be rejected. */ /* check if other audio channel is started. If yes, start */ bdcpy(open.bd_addr, p_scb->peer_addr); open.chnl = p_scb->chnl; open.hndl = p_scb->hndl; open.status = BTA_AV_SUCCESS; open.starting = bta_av_chk_start(p_scb); open.edr = 0; if( NULL != (p = BTM_ReadRemoteFeatures(p_scb->peer_addr))) { if(HCI_EDR_ACL_2MPS_SUPPORTED(p)) open.edr |= BTA_AV_EDR_2MBPS; if(HCI_EDR_ACL_3MPS_SUPPORTED(p)) open.edr |= BTA_AV_EDR_3MBPS; } #if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE) bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr); #endif if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) open.sep = AVDT_TSEP_SNK; else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) open.sep = AVDT_TSEP_SRC; (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open);//繼續向上回調 ... } ...
那上面的回調是在哪里注冊的呢?是在bta_av_api_enable的時候,也就是從btif 層進行av enable的時候 傳下來的.就是bte_av_callback,
static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data) { btif_transfer_context(btif_av_handle_event, event, (char*)p_data, sizeof(tBTA_AV), btif_av_event_deep_copy); }
發現回調還是回到btif 層,
static void btif_av_handle_event(UINT16 event, char* p_param) { btif_sm_dispatch(btif_av_cb.sm_handle, event, (void*)p_param); btif_av_event_free_data(event, p_param); }
到這里,我們見到了 我們一直關心的btif層面的AV 狀態機:,event = BTA_AV_OPEN_EVT
/***************************************************************************** ** ** Function btif_sm_dispatch ** ** Description Dispatches the 'event' along with 'data' to the current state handler ** ** Returns BT_STATUS_SUCCESS on success ** BT_STATUS_UNHANDLED if event was not processed ** BT_STATUS_FAIL otherwise ** ******************************************************************************/ bt_status_t btif_sm_dispatch(btif_sm_handle_t handle, btif_sm_event_t event, void *data) { bt_status_t status = BT_STATUS_SUCCESS; btif_sm_cb_t *p_cb = (btif_sm_cb_t*)handle; if (p_cb == NULL) { BTIF_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); return BT_STATUS_FAIL; } if (p_cb->p_handlers[p_cb->state](event, data) == FALSE)//進入當前的狀態handler進行處理,當前是opening return BT_STATUS_UNHANDLED; return status; }
也就是btif_av_state_opening_handler ,我們繼續看:
/***************************************************************************** ** ** Function btif_av_state_opening_handler ** ** Description Intermediate state managing events during establishment ** of avdtp channel ** ** Returns TRUE if event was processed, FALSE otherwise ** *******************************************************************************/ static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data) { BTIF_TRACE_DEBUG("%s event:%s flags %x", __FUNCTION__, dump_av_sm_event_name(event), btif_av_cb.flags); switch (event) { ... case BTA_AV_OPEN_EVT: { tBTA_AV *p_bta_data = (tBTA_AV*)p_data; btav_connection_state_t state; btif_sm_state_t av_state; if (p_bta_data->open.status == BTA_AV_SUCCESS) { state = BTAV_CONNECTION_STATE_CONNECTED; av_state = BTIF_AV_STATE_OPENED;//設置狀態 btif_av_cb.edr = p_bta_data->open.edr; btif_av_cb.peer_sep = p_bta_data->open.sep; btif_a2dp_set_peer_sep(p_bta_data->open.sep); } else { ... } /* inform the application of the event */ btif_report_connection_state(state, &(btif_av_cb.peer_bda));//通知上層:HAL_CBACK(bt_av_src_callbacks, connection_state_cb, state, bd_addr); /* change state to open/idle based on the status */ btif_sm_change_state(btif_av_cb.sm_handle, av_state);//這里就是狀態機改變狀態的地方 if (btif_av_cb.peer_sep == AVDT_TSEP_SNK) { /* if queued PLAY command, send it now */ btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr, (p_bta_data->open.status == BTA_AV_SUCCESS)); } else if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) { ... } btif_queue_advance(); } ...
我們分析一下btif_sm_change_state:
/***************************************************************************** ** ** Function btif_sm_change_state ** ** Description Make a transition to the new 'state'. The 'BTIF_SM_EXIT_EVT' ** shall be invoked before exiting the current state. The ** 'BTIF_SM_ENTER_EVT' shall be invoked before entering the new state ** ** Returns BT_STATUS_SUCCESS on success ** BT_STATUS_UNHANDLED if event was not processed ** BT_STATUS_FAIL otherwise ** ******************************************************************************/ bt_status_t btif_sm_change_state(btif_sm_handle_t handle, btif_sm_state_t state) { bt_status_t status = BT_STATUS_SUCCESS; btif_sm_cb_t *p_cb = (btif_sm_cb_t*)handle; if (p_cb == NULL) { BTIF_TRACE_ERROR("%s : Invalid handle", __FUNCTION__); return BT_STATUS_FAIL; } /* Send exit event to the current state */ if (p_cb->p_handlers[p_cb->state](BTIF_SM_EXIT_EVT, NULL) == FALSE)//先退出原先的狀態 status = BT_STATUS_UNHANDLED; /* Change to the new state */ p_cb->state = state; /* Send enter event to the new state */ if (p_cb->p_handlers[p_cb->state](BTIF_SM_ENTER_EVT, NULL) == FALSE)//進入到新的狀態 status = BT_STATUS_UNHANDLED; return status; }
新的狀態就是BTIF_AV_STATE_OPENED 那到這里, 所有涉及到的狀態機都已經是open 狀態了.
這里補充一張a2dp 各級狀態機的輪轉圖:
那么關於a2dp profile的連接的流程就分析到這里了.