欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

A2dp 连接过程源代码分析

最编程 2024-04-03 15:56:14
...
【直播预告】国产数据库,一半都是花架子?”

在上一篇文章中,我们已经分析了: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;

                					

推荐阅读