zoukankan      html  css  js  c++  java
  • A2dp连接流程源码分析

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

     这里做了两件事:

    1. 发送BTA_AV_AVDT_CONNECT_EVT 到btu task
    2. 执行回调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的连接的流程就分析到这里了.


  • 相关阅读:
    select移动选项
    jFinal+AngularJs未来javaEE开发的趋势——程序员的福音 .
    MVC框架PK:Angular、Backbone、CanJS与Ember
    错误 1093 You can't specify target table 'table name' for update in FROM clause
    Angularjs开发一些经验总结
    需求调研的步骤、方法
    MyEclipse如何跟踪调试
    需求入门: 软件需求的三个层次
    JAVA的Random类(转)
    Java中从[1,36]随机生成7个不重复的数字,放入一个数组中
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9406230.html
Copyright © 2011-2022 走看看