A2dp初始化流程源碼分析


藍牙啟動的時候,會涉及到各個profile 的啟動。這篇文章分析一下,藍牙中a2dp profile的初始化流程。

我們從AdapterState.java中對於USER_TURN_ON 消息的處理說起:

switch(msg.what) {
               case USER_TURN_ON:
                   notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON);
                   mPendingCommandState.setTurningOn(true);
                   transitionTo(mPendingCommandState);
                   sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY);
                   adapterService.startCoreServices();//開始啟動核心的服務,就是各種profile
                   break;

繼續看:

    void startCoreServices()
    {
        debugLog("startCoreServices()");
        Class[] supportedProfileServices = Config.getSupportedProfiles();

        //Start profile services
        if (!mProfilesStarted && supportedProfileServices.length >0) {
            //Startup all profile services
       setProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);//start all profile
        }
...
    }

 

看看setProfileServiceState的實現,他就是實現一個for 循環,在里面啟動所有的profile:

    private void setProfileServiceState(Class[] services, int state) {
        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
            debugLog("setProfileServiceState() - Invalid state, leaving...");
            return;
        }

        int expectedCurrentState= BluetoothAdapter.STATE_OFF;
        int pendingState = BluetoothAdapter.STATE_TURNING_ON;
        if (state == BluetoothAdapter.STATE_OFF) {
            expectedCurrentState= BluetoothAdapter.STATE_ON;
            pendingState = BluetoothAdapter.STATE_TURNING_OFF;
        }

        for (int i=0; i <services.length;i++) {
            String serviceName = services[i].getName();
            String simpleName = services[i].getSimpleName();

            if (simpleName.equals("GattService")) continue;

            Integer serviceState = mProfileServicesState.get(serviceName);
            if(serviceState != null && serviceState != expectedCurrentState) {
                debugLog("setProfileServiceState() - Unable to "
                    + (state == BluetoothAdapter.STATE_OFF ? "start" : "stop" )
                    + " service " + serviceName
                    + ". Invalid state: " + serviceState);
                continue;
            }

            debugLog("setProfileServiceState() - "
                + (state == BluetoothAdapter.STATE_OFF ? "Stopping" : "Starting")
                + " service " + serviceName);

            mProfileServicesState.put(serviceName,pendingState);
            Intent intent = new Intent(this,services[i]);
            intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
            intent.putExtra(BluetoothAdapter.EXTRA_STATE,state);
            startService(intent);//啟動服務
        }
    }

 

startSerice 啟動服務,我們這里只分析a2dp的情況,其他的profile的啟動情況類似。a2dp 對應的service 文件是a2dpService.java,下面看看service的啟動:

    public void onCreate() {
        if (DBG) log("onCreate");
        super.onCreate();
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mBinder = initBinder();//生成一個Binder
        mAdapterService = AdapterService.getAdapterService();
        if (mAdapterService != null) {
            mAdapterService.addProfile(this);
        } else {
            Log.w(TAG, "onCreate, null mAdapterService");
        }
    }

 

我們看看a2dpService是如何實現這個mBInder的:

    protected IProfileServiceBinder initBinder() {
        return new BluetoothA2dpBinder(this);//傳入一個 this 指針,那么也就是說a2dpService 這個類本身就是這個service
    }

 

看看這個BluetoothA2dpBinder  是一個什么樣的接口?其實他就是對原本的服務的一個封裝,包含了原來的服務

    //Binder object: Must be static class or memory leak may occur 
    private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 
        implements IProfileServiceBinder {
        private A2dpService mService;

        private A2dpService getService() {
            if (!Utils.checkCaller()) {
                Log.w(TAG,"A2dp call not allowed for non-active user");
                return null;
            }

            if (mService != null && mService.isAvailable()) {
                return mService;
            }
            return null;
        }

        BluetoothA2dpBinder(A2dpService svc) {
            mService = svc;//構造函數傳入的this 參數
        }

        public boolean cleanup()  {
            mService = null;
            return true;
        }

        public boolean connect(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return false;
            return service.connect(device);
        }

        public boolean disconnect(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return false;
            return service.disconnect(device);
        }

        public List<BluetoothDevice> getConnectedDevices() {
            A2dpService service = getService();
            if (service == null) return new ArrayList<BluetoothDevice>(0);
            return service.getConnectedDevices();
        }

        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
            A2dpService service = getService();
            if (service == null) return new ArrayList<BluetoothDevice>(0);
            return service.getDevicesMatchingConnectionStates(states);
        }

        public int getConnectionState(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
            return service.getConnectionState(device);
        }

        public boolean setPriority(BluetoothDevice device, int priority) {
            A2dpService service = getService();
            if (service == null) return false;
            return service.setPriority(device, priority);
        }

        public int getPriority(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
            return service.getPriority(device);
        }

        public boolean isAvrcpAbsoluteVolumeSupported() {
            A2dpService service = getService();
            if (service == null) return false;
            return service.isAvrcpAbsoluteVolumeSupported();
        }

        public void adjustAvrcpAbsoluteVolume(int direction) {
            A2dpService service = getService();
            if (service == null) return;
            service.adjustAvrcpAbsoluteVolume(direction);
        }

        public void setAvrcpAbsoluteVolume(int volume) {
            A2dpService service = getService();
            if (service == null) return;
            service.setAvrcpAbsoluteVolume(volume);
        }

        public boolean isA2dpPlaying(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return false;
            return service.isA2dpPlaying(device);
        }
    }

 

這個mBinder 會在別的應用程序綁定的時候,返回給對方。所以其就是對原本服務的一個封裝。

接着我們看onStartCommand:

    public int onStartCommand(Intent intent, int flags, int startId) {
...
            String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
            if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                if(state==BluetoothAdapter.STATE_OFF) {
                    Log.d(mName, "Received stop request...Stopping profile...");
                    doStop(intent);
                } else if (state == BluetoothAdapter.STATE_ON) {
                    Log.d(mName, "Received start request. Starting profile...");
                    doStart(intent);//啟動
                }
            }
        return PROFILE_SERVICE_MODE;
    }

 

繼續看:

    private void doStart(Intent intent) {
        //Start service
        if (mAdapter == null) {
            Log.e(mName, "Error starting profile. BluetoothAdapter is null");
        } else {
            if (DBG) log("start()");
            mStartError = !start();//這個是虛函數,看看a2dpService 具體如何實現的
            if (!mStartError) {
                notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON);上報狀態
            } else {
                Log.e(mName, "Error starting profile. BluetoothAdapter is null");
            }
        }
    }

 

上報的狀態的部分就不分析了,我們直接看 start的部分:
avrcp的部分暫時略過,

    protected boolean start() {
        mAvrcp = Avrcp.make(this);
        mStateMachine = A2dpStateMachine.make(this, this);//啟動狀態機
        setA2dpService(this);//設置a2dpService服務為本身sAd2dpService = this
        return true;
    }

 

看看狀態機make 函數到底干嘛?猜想應該是初始化狀態機:

    static A2dpStateMachine make(A2dpService svc, Context context) {
        A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context);//新建一個狀態機
        a2dpSm.start();//start
        return a2dpSm;
    }

 

這里的start就是讓狀態機轉起來,和a2dp 的關系不大,這里不分析 了,我們這里的重頭戲是new A2dpStateMachine,這里涉及到一些 變量、狀態的初始化,以及協議棧中關於a2dp的初始化。

    private A2dpStateMachine(A2dpService svc, Context context) {
        super("A2dpStateMachine");
        mService = svc;//a2dpService
        mContext = context;//a2dpService
        mAdapter = BluetoothAdapter.getDefaultAdapter();

        initNative();//native 函數

        mDisconnected = new Disconnected();//新建各種狀態,類中類
        mPending = new Pending();
        mConnected = new Connected();

        addState(mDisconnected);//往狀態機中添加狀態
        addState(mPending);
        addState(mConnected);

        setInitialState(mDisconnected);//設置初始狀態

        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService");

        mIntentBroadcastHandler = new IntentBroadcastHandler();

        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

 

如果我們不往JNI 層以下分析的話,那么應用層的Service的初始化的流程已經完成了。

這里調用initNative其實就是 對JNI 層以及協議棧進行a2dp 的初始化,我們這里也分析一下:

static void initNative(JNIEnv *env, jobject object) {
    const bt_interface_t* btInf;
    bt_status_t status;
...
    if ( (sBluetoothA2dpInterface = (btav_interface_t *)
          btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {//獲取a2dp的接口
        ALOGE("Failed to get Bluetooth A2DP Interface");
        return;
    }

    if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {//對接口進行初始化
        ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
        sBluetoothA2dpInterface = NULL;
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);
}

 

我們先看一下 這個接口實現在那里,其實現在btif_av.c中,從這里開始就是C代碼實現的了。

static const btav_interface_t bt_av_src_interface = {
    .size = sizeof(btav_interface_t),
    .init = init_src,
    .connect = src_connect_sink,
    .disconnect = disconnect,
    .cleanup = cleanup_src,
};

 

 那我們繼續看 其init的實現:

/*******************************************************************************
**
** Function         init_src
**
** Description      Initializes the AV interface for source mode
**
** Returns          bt_status_t
**
*******************************************************************************/

static bt_status_t init_src(btav_callbacks_t* callbacks)
{
    bt_status_t status = btif_av_init();
    if (status == BT_STATUS_SUCCESS)
        bt_av_src_callbacks = callbacks;//保存JNI層的callback

 

上面的callback 是JNI 層 注冊下來的,猜想,應該都是一些狀態上報的callback,我們看看 都有哪些函數:

static btav_callbacks_t sBluetoothA2dpCallbacks = {
    sizeof(sBluetoothA2dpCallbacks),
    bta2dp_connection_state_callback,//連接狀態上報
    bta2dp_audio_state_callback//audio的狀態上報
};

 

下面我們關注一下btif_av_init的實現:

/*******************************************************************************
**
** Function         btif_av_init
**
** Description      Initializes btif AV if not already done
**
** Returns          bt_status_t
**
*******************************************************************************/

bt_status_t btif_av_init()
{
    if (btif_av_cb.sm_handle == NULL)
    {
        if (!btif_a2dp_start_media_task())//開啟media 的線程
            return BT_STATUS_FAIL;

        /* Also initialize the AV state machine */
        btif_av_cb.sm_handle =
                btif_sm_init((const btif_sm_handler_t*)btif_av_state_handlers, BTIF_AV_STATE_IDLE);//初始化協議棧層面的btif里的狀態機

        btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID);//enable source service

#if (BTA_AV_SINK_INCLUDED == TRUE)
        btif_enable_service(BTA_A2DP_SINK_SERVICE_ID);
        btif_a2dp_on_init();//啥也沒干
    }

    return BT_STATUS_SUCCESS;
}

 

這里還涉及到sink的部分,暫時不講。

上面的代碼流程分為兩個部分:

  1. 開啟media task的流程
  2. btif_sm 的初始化
  3. btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID)

我們先看第一部分:

開啟media task的流程

bool btif_a2dp_start_media_task(void)
{
...
    btif_media_cmd_msg_queue = fixed_queue_new(SIZE_MAX);//新建一個隊列用於處理media 相關的cmd

    /* start a2dp media task */
    worker_thread = thread_new("media_worker");//新建一個media_worker 縣城

    fixed_queue_register_dequeue(btif_media_cmd_msg_queue,
        thread_get_reactor(worker_thread),
        btif_media_thread_handle_cmd,
        NULL);將隊列和thread綁定

    thread_post(worker_thread, btif_media_thread_init, NULL);//post 到media 線程中繼續media 線程的init 行為

    APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##");
    return true;
}

 

這里我們看看,會有哪些cmd 會塞到這個隊列里面去處理:

/* BTIF media cmd event definition : BTIF_MEDIA_TASK_CMD */
enum
{
    BTIF_MEDIA_START_AA_TX = 1,
    BTIF_MEDIA_STOP_AA_TX,
    BTIF_MEDIA_AA_RX_RDY,
    BTIF_MEDIA_UIPC_RX_RDY,
    BTIF_MEDIA_SBC_ENC_INIT,
    BTIF_MEDIA_SBC_ENC_UPDATE,
    BTIF_MEDIA_SBC_DEC_INIT,
    BTIF_MEDIA_VIDEO_DEC_INIT,
    BTIF_MEDIA_FLUSH_AA_TX,
    BTIF_MEDIA_FLUSH_AA_RX,
    BTIF_MEDIA_AUDIO_FEEDING_INIT,
    BTIF_MEDIA_AUDIO_RECEIVING_INIT,
    BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE,
    BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK
}

 

下面我們分析下 btif_media_thread_init都做了哪些事情:

static void btif_media_thread_init(UNUSED_ATTR void *context) {
  memset(&btif_media_cb, 0, sizeof(btif_media_cb));
  UIPC_Init(NULL);

#if (BTA_AV_INCLUDED == TRUE)
  UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb);//打開了audio的控制通道
#endif

  raise_priority_a2dp(TASK_HIGH_MEDIA);//提升優先級
  media_task_running = MEDIA_TASK_STATE_ON;//標志線程狀態
}

 

我們現在看看

btif_sm 的初始化

/*****************************************************************************
**
** Function     btif_sm_init
**
** Description  Initializes the state machine with the state handlers
**              The caller should ensure that the table and the corresponding
**              states match. The location that 'p_handlers' points to shall
**              be available until the btif_sm_shutdown API is invoked.
**
** Returns      Returns a pointer to the initialized state machine handle.
**
******************************************************************************/

btif_sm_handle_t btif_sm_init(const btif_sm_handler_t *p_handlers, btif_sm_state_t initial_state)
{
    btif_sm_cb_t *p_cb;

    if (p_handlers == NULL)
    {
        BTIF_TRACE_ERROR("%s : p_handlers is NULL", __FUNCTION__);
        return NULL;
    }

    p_cb = (btif_sm_cb_t *)osi_malloc(sizeof(btif_sm_cb_t));
    p_cb->state = initial_state;//初始狀態
    p_cb->p_handlers = (btif_sm_handler_t*)p_handlers;一組函數指針

    /* Send BTIF_SM_ENTER_EVT to the initial state */
    p_cb->p_handlers[initial_state](BTIF_SM_ENTER_EVT, NULL);//進入到初始狀態

    return (btif_sm_handle_t)p_cb;
}

 

這個函數 是要返回一個btif_sm_handle_t 結構給btif_av_cb.sm_handle,這個結構,里面包含一個state以及函數指針 ,如下:

typedef struct {
    btif_sm_state_t         state;
    btif_sm_handler_t       *p_handlers;
} btif_sm_cb_t;

 

我們現在看看,這個函數進行初始化的時候傳入的函數指針 有哪些:

static const btif_sm_handler_t btif_av_state_handlers[] =
{
    btif_av_state_idle_handler,
    btif_av_state_opening_handler,
    btif_av_state_opened_handler,
    btif_av_state_started_handler,
    btif_av_state_closing_handler
};

 

我們發現是 不同狀態下的處理句柄。最后我們看看(BTIF_SM_ENTER_EVT)進入初始狀態,有執行什么操作:

 

static BOOLEAN btif_av_state_idle_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 BTIF_SM_ENTER_EVT:
            /* clear the peer_bda */
            memset(&btif_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t));
            btif_av_cb.flags = 0;
            btif_av_cb.edr = 0;
            btif_a2dp_on_idle();//btif_media_cb 設置為idle,reset audio_codec_config
            break;

 

下面我們看看

btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID);

 

/*******************************************************************************
**
** Function         btif_enable_service
**
** Description      Enables the service 'service_ID' to the service_mask.
**                  Upon BT enable, BTIF core shall invoke the BTA APIs to
**                  enable the profiles
**
** Returns          bt_status_t
**
*******************************************************************************/
bt_status_t btif_enable_service(tBTA_SERVICE_ID service_id)
{
    tBTA_SERVICE_ID *p_id = &service_id;

    /* If BT is enabled, we need to switch to BTIF context and trigger the
     * enable for that profile
     *
     * Otherwise, we just set the flag. On BT_Enable, the DM will trigger
     * enable for the profiles that have been enabled */

    btif_enabled_services |= (1 << service_id);//更新此全局變量


    if (btif_is_enabled())
    {
        btif_transfer_context(btif_dm_execute_service_request,
                              BTIF_DM_ENABLE_SERVICE,
                              (char*)p_id, sizeof(tBTA_SERVICE_ID), NULL);
    }

    return BT_STATUS_SUCCESS;
}

 

我們繼續看btif_dm_execute_service_request:

void btif_dm_execute_service_request(UINT16 event, char *p_param)
{
    BOOLEAN b_enable = FALSE;
    bt_status_t status;
    if (event == BTIF_DM_ENABLE_SERVICE)
    {
        b_enable = TRUE;
    }
    status = btif_in_execute_service_request(*((tBTA_SERVICE_ID*)p_param), b_enable);
    if (status == BT_STATUS_SUCCESS)
    {
        bt_property_t property;
        bt_uuid_t local_uuids[BT_MAX_NUM_UUIDS];

        /* Now send the UUID_PROPERTY_CHANGED event to the upper layer */
        BTIF_STORAGE_FILL_PROPERTY(&property, BT_PROPERTY_UUIDS,
                                    sizeof(local_uuids), local_uuids);
        btif_storage_get_adapter_property(&property);
        HAL_CBACK(bt_hal_cbacks, adapter_properties_cb,
                          BT_STATUS_SUCCESS, 1, &property);//注意這里調用的是adapter_properties_cb,代表local的屬性,不是remote devices的
    }
    return;
}

 

我們繼續看btif_in_execute_service_request的實現:

bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id,
                                                BOOLEAN b_enable)
{
    BTIF_TRACE_DEBUG("%s service_id: %d", __FUNCTION__, service_id);
    /* Check the service_ID and invoke the profile's BT state changed API */
    switch (service_id)
    {
         case BTA_HFP_SERVICE_ID:
         case BTA_HSP_SERVICE_ID:
         {
              btif_hf_execute_service(b_enable);
         }break;
         case BTA_A2DP_SOURCE_SERVICE_ID:
         {
              btif_av_execute_service(b_enable);
         }break;

 

我們繼續看btif_av_execute_service:

/*******************************************************************************
**
** Function         btif_av_execute_service
**
** Description      Initializes/Shuts down the service
**
** Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
**
*******************************************************************************/
bt_status_t btif_av_execute_service(BOOLEAN b_enable)
{
     if (b_enable)
     {
         /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
          * handle this request in order to allow incoming connections to succeed.
          * We need to put this back once support for this is added */

         /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
          * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
          * be initiated by the app/audioflinger layers */

         BTA_AvEnable(BTA_SEC_AUTHENTICATE,     BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR|BTA_AV_FEAT_NO_SCO_SSPD|BTA_AV_FEAT_RCCT|BTA_AV_FEAT_ADV_CTRL,bte_av_callback);//enable

         BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0, bte_av_media_callback);//register
     }
     else {
         BTA_AvDeregister(btif_av_cb.bta_handle);
         BTA_AvDisable();
     }
     return BT_STATUS_SUCCESS;
}

 

 上面的流程又可以分為兩個部分:

  1. BTA_AvEnable
  2. BTA_AvRegister

我們下面分別來分析這兩個部分:

BTA_AvEnable

 

void BTA_AvEnable(tBTA_SEC sec_mask, tBTA_AV_FEAT features, tBTA_AV_CBACK *p_cback)
{
    tBTA_AV_API_ENABLE  *p_buf;

    /* register with BTA system manager */
    bta_sys_register(BTA_ID_AV, &bta_av_reg);//注冊了BTA_ID_AV模塊

    if ((p_buf = (tBTA_AV_API_ENABLE *) GKI_getbuf(sizeof(tBTA_AV_API_ENABLE))) != NULL)
    {
        p_buf->hdr.event = BTA_AV_API_ENABLE_EVT;
        p_buf->p_cback  = p_cback;
        p_buf->features = features;
        p_buf->sec_mask = sec_mask;
        bta_sys_sendmsg(p_buf);
    }
}

 

這里依然是熟悉的 線程間通信,這里發出的第一個event 是BTA_AV_API_ENABLE_EVT,

AV nsm event=0x122b(API_ENABLE)

 

處理這個狀態機的函數是bta_av_hdl_event,就是上面 剛剛注冊到sys里面的:

看看其實現:

/*******************************************************************************
**
** Function         bta_av_hdl_event
**
** Description      Advanced audio/video main event handling function.
**
**
** Returns          BOOLEAN
**
*******************************************************************************/
BOOLEAN bta_av_hdl_event(BT_HDR *p_msg)
{
    UINT16 event = p_msg->event;
    UINT16 first_event = BTA_AV_FIRST_NSM_EVT;

    if (event > BTA_AV_LAST_EVT)
    {
        return TRUE; /* to free p_msg */
    }

    if(event >= first_event)//BTA_AV_FIRST_NSM_EVT,這里不會在狀態機中處理
    {
        /* non state machine events */
        (*bta_av_nsm_act[event - BTA_AV_FIRST_NSM_EVT]) ((tBTA_AV_DATA *) p_msg);
    }
    else if (event >= BTA_AV_FIRST_SM_EVT && event <= BTA_AV_LAST_SM_EVT)//handled by the AV main state machine
    {
        /* state machine events */
        bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA *) p_msg);
    }
    else   //這里處理by AV stream state machine
    {
        /* stream state machine events */
        bta_av_ssm_execute( bta_av_hndl_to_scb(p_msg->layer_specific),
                                p_msg->event, (tBTA_AV_DATA *) p_msg);
    }
    return TRUE;
}

 

分析上面的bta_av_hdl_event 處理,發現其把event 分成了三類,

  1. 一種是在AV main state machine里面處理的,即調用bta_av_sm_execute 來處理
  2. 第二種是stream state machine 里面處理的,即調用bta_av_ssm_execute 
  3. 第三種是不在狀態機里面處理,即調用bta_av_nsm_act 里面的函數來處理

下面我們是各種情況對應的event:

bta_av_sm_execute處理如下的event:

 /* these events are handled by the AV main state machine */
    BTA_AV_API_DISABLE_EVT = BTA_SYS_EVT_START(BTA_ID_AV),//0x1200
    BTA_AV_API_REMOTE_CMD_EVT,
    BTA_AV_API_VENDOR_CMD_EVT,
    BTA_AV_API_VENDOR_RSP_EVT,
    BTA_AV_API_META_RSP_EVT,
    BTA_AV_API_RC_CLOSE_EVT,
    BTA_AV_AVRC_OPEN_EVT,
    BTA_AV_AVRC_MSG_EVT,
    BTA_AV_AVRC_NONE_EVT,//0x1208

 

bta_av_ssm_execute 處理如下的event:

 /* these events are handled by the AV stream state machine */
    BTA_AV_API_OPEN_EVT,//0x1209
    BTA_AV_API_CLOSE_EVT,
    BTA_AV_AP_START_EVT,   //0x120b     /* the following 2 events must be in the same order as the *API_*EVT */
    BTA_AV_AP_STOP_EVT,            /*其含義就是從API的相關的狀態直接跳轉到相應的狀態機中執行*/
    BTA_AV_API_RECONFIG_EVT,
    BTA_AV_API_PROTECT_REQ_EVT,
    BTA_AV_API_PROTECT_RSP_EVT,
    BTA_AV_API_RC_OPEN_EVT,
    BTA_AV_SRC_DATA_READY_EVT,
    BTA_AV_CI_SETCONFIG_OK_EVT,
    BTA_AV_CI_SETCONFIG_FAIL_EVT,
    BTA_AV_SDP_DISC_OK_EVT,
    BTA_AV_SDP_DISC_FAIL_EVT,
    BTA_AV_STR_DISC_OK_EVT,
    BTA_AV_STR_DISC_FAIL_EVT,
    BTA_AV_STR_GETCAP_OK_EVT,
    BTA_AV_STR_GETCAP_FAIL_EVT,
    BTA_AV_STR_OPEN_OK_EVT,
    BTA_AV_STR_OPEN_FAIL_EVT,
    BTA_AV_STR_START_OK_EVT,
    BTA_AV_STR_START_FAIL_EVT,
    BTA_AV_STR_CLOSE_EVT,
    BTA_AV_STR_CONFIG_IND_EVT,
    BTA_AV_STR_SECURITY_IND_EVT,
    BTA_AV_STR_SECURITY_CFM_EVT,
    BTA_AV_STR_WRITE_CFM_EVT,
    BTA_AV_STR_SUSPEND_CFM_EVT,
    BTA_AV_STR_RECONFIG_CFM_EVT,
    BTA_AV_AVRC_TIMER_EVT,
    BTA_AV_AVDT_CONNECT_EVT,
    BTA_AV_AVDT_DISCONNECT_EVT,
    BTA_AV_ROLE_CHANGE_EVT,
    BTA_AV_AVDT_DELAY_RPT_EVT,
    BTA_AV_ACP_CONNECT_EVT,

 

bta_av_nsm_act 處理如下的event:

    /* these events are handled outside of the state machine */
    BTA_AV_API_ENABLE_EVT,
    BTA_AV_API_REGISTER_EVT,
    BTA_AV_API_DEREGISTER_EVT,
    BTA_AV_API_DISCONNECT_EVT,
    BTA_AV_CI_SRC_DATA_READY_EVT,
    BTA_AV_SIG_CHG_EVT,
    BTA_AV_SIG_TIMER_EVT,
    BTA_AV_SDP_AVRC_DISC_EVT,
    BTA_AV_AVRC_CLOSE_EVT,
    BTA_AV_CONN_CHG_EVT,
    BTA_AV_DEREG_COMP_EVT,
#if (BTA_AV_SINK_INCLUDED == TRUE)
    BTA_AV_API_SINK_ENABLE_EVT,
#endif
#if (AVDT_REPORTING == TRUE)
    BTA_AV_AVDT_RPT_CONN_EVT,
#endif
    BTA_AV_API_START_EVT,  //0x1238 

 

 下面我們繼續看BTA_AV_API_ENABLE_EVT的處理情況:

/*******************************************************************************
**
** Function         bta_av_api_enable
**
** Description      Handle an API enable event.
**
**
** Returns          void
**
*******************************************************************************/
static void bta_av_api_enable(tBTA_AV_DATA *p_data)
{
    int i;
    tBTA_AV_ENABLE      enable;

    /* initialize control block */
    memset(&bta_av_cb, 0, sizeof(tBTA_AV_CB));//bta_av_cb的生命線從此刻開始

    for(i=0; i<BTA_AV_NUM_RCB; i++)
        bta_av_cb.rcb[i].handle = BTA_AV_RC_HANDLE_NONE;//rcb avrcp control block

    bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;

    /* store parameters */
    bta_av_cb.p_cback  = p_data->api_enable.p_cback;//bte_av_callback
    bta_av_cb.features = p_data->api_enable.features;
    bta_av_cb.sec_mask = p_data->api_enable.sec_mask;

    enable.features = bta_av_cb.features;

    /* Register for SCO change event */
    if (!(bta_av_cb.features & BTA_AV_FEAT_NO_SCO_SSPD))
    {
        bta_sys_sco_register(bta_av_sco_chg_cback);
    }

    /* call callback with enable event */
    (*bta_av_cb.p_cback)(BTA_AV_ENABLE_EVT, (tBTA_AV *)&enable);//調用回調,預示av enable 完成
}

 

回調函數處理的流程是 找對對應狀態的handler 來處理event,當前的btif_av狀態是idle對於BTA_AV_ENABLE_EVT  沒有處理,這里就不分析了。

下面我們看看

 BTA_AvRegister

 

/*******************************************************************************
**
** Function         BTA_AvRegister
**
** Description      Register the audio or video service to stack. When the
**                  operation is complete the callback function will be
**                  called with a BTA_AV_REGISTER_EVT. This function must
**                  be called before AVDT stream is open.
**
**
** Returns          void
**
*******************************************************************************/
void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id, tBTA_AV_DATA_CBACK  *p_data_cback)
{
    tBTA_AV_API_REG  *p_buf;


    if ((p_buf = (tBTA_AV_API_REG *) GKI_getbuf(sizeof(tBTA_AV_API_REG))) != NULL)
    {
        p_buf->hdr.layer_specific   = chnl;
        p_buf->hdr.event = BTA_AV_API_REGISTER_EVT;
        if(p_service_name)
        {
            BCM_STRNCPY_S(p_buf->p_service_name, sizeof(p_buf->p_service_name), p_service_name, BTA_SERVICE_NAME_LEN);
            p_buf->p_service_name[BTA_SERVICE_NAME_LEN-1] = 0;//保存名字
        }
        else
        {
            p_buf->p_service_name[0] = 0;
        }
        p_buf->app_id = app_id;
        p_buf->p_app_data_cback = p_data_cback;//保存callback
        bta_sys_sendmsg(p_buf);
    }
}

 

這里還是熟悉的線程間通信,由上面的分析得知,這個事件是由bta_av_nsm_act  來處理:

我們看看具體的實現,這個函數非常的長,里面主要涉及 結構的初始化,以及將結構注冊到AVDTP,

/*******************************************************************************
**
** Function         bta_av_api_register
**
** Description      allocate stream control block,
**                  register the service to stack
**                  create SDP record
**
** Returns          void
**
*******************************************************************************/
static void bta_av_api_register(tBTA_AV_DATA *p_data)
{
    tBTA_AV_REGISTER    registr;
    tBTA_AV_SCB         *p_scb;    /* stream control block */
    tAVDT_REG       reg;
    tAVDT_CS        cs;
    char            *p_service_name;
    tBTA_AV_CODEC   codec_type;
    tBTA_UTL_COD    cod;
    UINT8           index = 0;
    char p_avk_service_name[BTA_SERVICE_NAME_LEN+1];
    BCM_STRNCPY_S(p_avk_service_name, sizeof(p_avk_service_name), BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN);//sink service name

    memset(&cs,0,sizeof(tAVDT_CS));//初始化cs,該項最終拷貝到p_scb->cfg,並且AVDT_CreateStream 會使用到

    registr.status = BTA_AV_FAIL_RESOURCES;//register 記錄注冊的信息,回調的時候會上報
    registr.app_id = p_data->api_reg.app_id;//0
    registr.chnl   = (tBTA_AV_CHNL)p_data->hdr.layer_specific;//audio 0x40
    do   //do...while(0)結構
    {
        p_scb = bta_av_alloc_scb(registr.chnl);//分配了一個 stream control block  :bta_av_cb.p_scb[xx] = p_scb

        registr.hndl    = p_scb->hndl;//0x41 or 0x42  --- hndl = (tBTA_AV_HNDL)((xx + 1) | chnl);
        p_scb->app_id   = registr.app_id;//0

        /* initialize the stream control block */
        p_scb->timer.p_cback = (TIMER_CBACK*)&bta_av_timer_cback;
        registr.status = BTA_AV_SUCCESS;

        if((bta_av_cb.reg_audio + bta_av_cb.reg_video) == 0)//開始沒有注冊等於0
        {
            /* the first channel registered. register to AVDTP */
            reg.ctrl_mtu = p_bta_av_cfg->sig_mtu;
            reg.ret_tout = BTA_AV_RET_TOUT;
            reg.sig_tout = BTA_AV_SIG_TOUT;
            reg.idle_tout = BTA_AV_IDLE_TOUT;
            reg.sec_mask = bta_av_cb.sec_mask;
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
            bta_ar_reg_avdt(&reg, bta_av_conn_cback, BTA_ID_AV);//AR module registration to AVDT.
#endif
            bta_sys_role_chg_register(&bta_av_sys_rs_cback);

            /* create remote control TG service if required */
            if (bta_av_cb.features & (BTA_AV_FEAT_RCTG))
            {
                /* register with no authorization; let AVDTP use authorization instead */
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
                bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu,
                                (UINT8)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)), BTA_ID_AV);//注冊到AVCT
#endif

                bta_ar_reg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target", NULL,
                                p_bta_av_cfg->avrc_tg_cat, BTA_ID_AV);//給AVRCP注冊一個sdp record
#endif
            }

            /* Set the Capturing service class bit */

            cod.service = BTM_COD_SERVICE_CAPTURING;
            utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS);
        } /* if 1st channel */

        /* get stream configuration and create stream */
        /* memset(&cs.cfg,0,sizeof(tAVDT_CFG)); */
        cs.cfg.num_codec = 1;
        cs.tsep = AVDT_TSEP_SRC;//src端

        /*
         * memset of cs takes care setting call back pointers to null.
        cs.p_data_cback = NULL;
        cs.p_report_cback = NULL;
        */
        cs.nsc_mask = AVDT_NSC_RECONFIG |
              ((bta_av_cb.features & BTA_AV_FEAT_PROTECT) ? 0 : AVDT_NSC_SECURITY);
        APPL_TRACE_DEBUG("nsc_mask: 0x%x", cs.nsc_mask);

         p_service_name = p_data->api_reg.p_service_name;
        
        p_scb->suspend_sup  = TRUE;
        p_scb->recfg_sup    = TRUE;

        cs.p_ctrl_cback  = bta_av_dt_cback[p_scb->hdi];//根據不同的index 會調用不同bta_av_streamX_cback
        if(registr.chnl == BTA_AV_CHNL_AUDIO)
        {
            /* set up the audio stream control block */
            p_scb->p_act_tbl = (const tBTA_AV_ACT *)bta_av_a2d_action;//保存action table
            p_scb->p_cos     = &bta_av_a2d_cos;
            p_scb->media_type= AVDT_MEDIA_AUDIO;
            cs.cfg.psc_mask  = AVDT_PSC_TRANS;
            cs.media_type    = AVDT_MEDIA_AUDIO;
            cs.mtu           = p_bta_av_cfg->audio_mtu;
            cs.flush_to      = L2CAP_DEFAULT_FLUSH_TO;

...
            /* keep the configuration in the stream control block */
            memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
            while(index < BTA_AV_MAX_SEPS &&
                (*bta_av_a2d_cos.init)(&codec_type, cs.cfg.codec_info,
                &cs.cfg.num_protect, cs.cfg.protect_info, index) == TRUE)//bta_av_co_audio_init to to initialize audio paths
            {

                if(AVDT_CreateStream(&p_scb->seps[index].av_handle, &cs) == AVDT_SUCCESS)//Create a stream endpoint.
                {
                    p_scb->seps[index].codec_type = codec_type;
                    index++;
                }
            }

            if(!bta_av_cb.reg_audio)//為profile 創建相關的sdp record
            {
                /* create the SDP records on the 1st audio channel */
                bta_av_cb.sdp_a2d_handle = SDP_CreateRecord();
                A2D_AddRecord(UUID_SERVCLASS_AUDIO_SOURCE, p_service_name, NULL,
                                  A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_handle);
                bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SOURCE);

                /* start listening when A2DP is registered */
                if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
                    bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);

                /* if the AV and AVK are both supported, it cannot support the CT role */
                if (bta_av_cb.features & (BTA_AV_FEAT_RCCT))
                {
...
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
                    /* create an SDP record as AVRC CT. */
                    bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL,
                           p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV);
#endif
                }
            }
            bta_av_cb.reg_audio |= BTA_AV_HNDL_TO_MSK(p_scb->hdi);
            APPL_TRACE_DEBUG("reg_audio: 0x%x",bta_av_cb.reg_audio);
        }
        else
        {
            /*vedio*/
        }
    } while (0);

    /* call callback with register event */
    (*bta_av_cb.p_cback)(BTA_AV_REGISTER_EVT, (tBTA_AV *)&registr);//btif_av_cb.bta_handle = ((tBTA_AV*)p_data)->registr.hndl 
}

 上面代碼的大部分已經 謝了注解,這里解釋一下兩點:

  1. bta_av_a2d_cos.init 
  2. AVDT_CreateStream 
  3. 關於AVDTP的注冊bta_ar_reg_avdt(&reg, bta_av_conn_cback, BTA_ID_AV) (其實這個應該算第一點,因為只是一些注冊性的內容,所以最后講)

我們先看一下bta_av_a2d_cos.init的實現:

/*******************************************************************************
 **
 ** Function         bta_av_co_audio_init
 **
 ** Description      This callout function is executed by AV when it is
 **                  started by calling BTA_AvRegister().  This function can be
 **                  used by the phone to initialize audio paths or for other
 **                  initialization purposes.
 **
 **
 ** Returns          Stream codec and content protection capabilities info.
 **
 *******************************************************************************/
BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_num_protect,
        UINT8 *p_protect_info, UINT8 index)
{
    FUNC_TRACE();

    APPL_TRACE_DEBUG("bta_av_co_audio_init: %d", index);

    /* By default - no content protection info */
    *p_num_protect = 0;
    *p_protect_info = 0;

    /* reset remote preference through setconfig */
    bta_av_co_cb.codec_cfg_setconfig.id = BTIF_AV_CODEC_NONE;

    switch (index)
    {
    case BTIF_SV_AV_AA_SBC_INDEX:

        /* Set up for SBC codec  for SRC*/
        *p_codec_type = BTA_AV_CODEC_SBC;

        /* This should not fail because we are using constants for parameters */
        A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_caps, p_codec_info);//初始化和audio相關的參數

        /* Codec is valid */
        return TRUE;
...
    default:
        /* Not valid */
        return FALSE;
    }
}

 

 

 我們再看一下AVDT_CreateStream 的行為:

/*******************************************************************************
**
** Function         AVDT_CreateStream
**
** Description      Create a stream endpoint.  After a stream endpoint is
**                  created an application can initiate a connection between
**                  this endpoint and an endpoint on a peer device.  In
**                  addition, a peer device can discover, get the capabilities,
**                  and connect to this endpoint.
**
**
** Returns          AVDT_SUCCESS if successful, otherwise error.
**
*******************************************************************************/
UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs)
{
    UINT16      result = AVDT_SUCCESS;
    tAVDT_SCB   *p_scb;

    /* Verify parameters; if invalid, return failure */
    if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) || (p_cs->p_ctrl_cback == NULL))
    {
        result = AVDT_BAD_PARAMS;
    }
    /* Allocate scb; if no scbs, return failure */
    else if ((p_scb = avdt_scb_alloc(p_cs)) == NULL)//分配avdt_cb.scb里面的節點
    {
        result = AVDT_NO_RESOURCES;
    }
    else
    {
        *p_handle = avdt_scb_to_hdl(p_scb);//分配的節點在avdt_cb.scb里面的位置+1 是handle的值-->p_scb->seps[index].av_handle
    }
    return result;
}

 

 發現這個AVDT_CreateStream,本質上就是在AVDTP層創建stream control block 節點,並把這個節點用一個handle和bta_av_cb.p_scb 里面的節點 相關聯。

最后我們來簡單看一下

關於AVDTP的注冊bta_ar_reg_avdt(&reg, bta_av_conn_cback, BTA_ID_AV) 

這里無非就是一些注冊性質的行為,我們簡單分析一下:

/*******************************************************************************
**
** Function         bta_ar_reg_avdt
**
** Description      AR module registration to AVDT.
**
** Returns          void
**
*******************************************************************************/
void bta_ar_reg_avdt(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback, tBTA_SYS_ID sys_id)
{
    UINT8   mask = 0;

    if (sys_id == BTA_ID_AV)
    {
        bta_ar_cb.p_av_conn_cback = p_cback;//保存回調bta_av_conn_cback,其作用暫時不管
        mask = BTA_AR_AV_MASK;
    }
    else if (sys_id == BTA_ID_AVK)
    {
        bta_ar_cb.p_avk_conn_cback = p_cback;
        mask = BTA_AR_AVK_MASK;
    }


    if (mask)
    {
        if (bta_ar_cb.avdt_registered == 0)
        {
            AVDT_Register(p_reg, bta_ar_avdt_cback);//注冊AVDTP
        }
        bta_ar_cb.avdt_registered |= mask;
    }
}

 

我們繼續分析 AVDTP_Register,

/*******************************************************************************
**
** Function         AVDT_Register
**
** Description      This is the system level registration function for the
**                  AVDTP protocol.  This function initializes AVDTP and
**                  prepares the protocol stack for its use.  This function
**                  must be called once by the system or platform using AVDTP
**                  before the other functions of the API an be used.
**
**
** Returns          void
**
*******************************************************************************/
void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback)
{
    /* register PSM with L2CAP */
    L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO *) &avdt_l2c_appl);

    /* set security level */
    BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);
    BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);

    /* do not use security on the media channel */
    BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);
    BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);

#if AVDT_REPORTING == TRUE
    /* do not use security on the reporting channel */
    BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
    BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
        AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
#endif

    /* initialize AVDTP data structures */
    avdt_scb_init();
    avdt_ccb_init();
    avdt_ad_init();

    /* copy registration struct */
    memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG));//保存reg信息到avdt_cb.rcb里面
    avdt_cb.p_conn_cback = p_cback;//保存回調bta_ar_avdt_cback,發現這個函數的實現就是調用bta_ar_cb.p_av_conn_cback
}

 下面我們分析一下,上面的初始化AVDTP 數據結構的三個函數,我們先看看avdt_scb_init,從名字上面看,應該是初始化stream control block,

/*******************************************************************************
**
** Function         avdt_scb_init
**
** Description      Initialize stream control block module.
**
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_scb_init(void)
{
    memset(&avdt_cb.scb[0], 0, sizeof(tAVDT_SCB) * AVDT_NUM_SEPS);
    avdt_cb.p_scb_act = (tAVDT_SCB_ACTION *) avdt_scb_action;//保存action,這個是為了之后狀態機中執行
}

 

繼續看avdt_ccb_init, 從名字上面看,應該是初始化channel control block,

 

/*******************************************************************************
**
** Function         avdt_ccb_init
**
** Description      Initialize channel control block module.
**
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_ccb_init(void)
{
    memset(&avdt_cb.ccb[0], 0, sizeof(tAVDT_CCB) * AVDT_NUM_LINKS);
    avdt_cb.p_ccb_act = (tAVDT_CCB_ACTION *) avdt_ccb_action;//為了后續在channel 狀態機中執行action 使用
}

 

最后,我們看看avdt_ad_init的實現:Initialize adaption layer 

/*******************************************************************************
**
** Function         avdt_ad_init
**
** Description      Initialize adaption layer.
**
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_ad_init(void)
{
    int             i;
    tAVDT_TC_TBL    *p_tbl = avdt_cb.ad.tc_tbl;
    memset(&avdt_cb.ad, 0, sizeof(tAVDT_AD));

    /* make sure the peer_mtu is a valid value */
    for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++)
    {
        p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
    }
}

 

關於a2dp的JNI以下的初始化的過程,這里補了一張圖片:

 

好了,關於BTA_AvRegister 就講到這里,a2dp的初始化的工作也已經完成了。 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM