天天看點

藍牙(三)a2dp連接配接

以下内容都是基于android6.0的基礎上講的,相對于android4.4架構有了很大改動。

先借用一下圖用一下。

藍牙(三)a2dp連接配接

上圖描述的是藍牙協定棧,通過該圖,檢視A2dp的代碼在協定棧的調用流程。其分層架構如下: 

藍牙(三)a2dp連接配接

1.藍牙的系統服務service通過JNI與bluedroid協定棧進行通信。協定棧分為兩層,Bluetooth Embedded System(BTE)和Bluetooth Application Layer(BTA)。這兩層和framework層應用進行通信。 

2.藍牙服務通過Binder IPC通信與應用程式互動。 

3.系統服務給開發者提供了擷取各種profile的接口。

一、接下來将深入Framework層講解藍牙的連接配接

    1)那就從connectInt()開始

synchronized void connectInt(LocalBluetoothProfile profile) {
        if (!ensurePaired()) {
            return;
        }
        if (profile.connect(mDevice)) {
            if (Utils.D) {
                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
            }
            return;
        }
        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
    }
           

2)若profile為a2dpprofile,則A2dpProfile.connect()

public boolean connect(BluetoothDevice device) {
        if (mService == null) return false;
        List<BluetoothDevice> sinks = getConnectedDevices();
        if (sinks != null) {
            for (BluetoothDevice sink : sinks) {
                mService.disconnect(sink);
            }
        }
        return mService.connect(device);
    }
           

上面的代碼就很好了解了,查詢已連接配接的裝置,然後斷開。隻有先把藍牙節點給釋放掉才能供給其它使用。

3)BluetoothA2dp.connect()

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;
    }
           

BluetoothA2dp類是幹什麼的呢?它是藍牙a2dp服務的代理對象,就是上層想要使用就必須用到它了。擷取BluetoothA2dp的方法就是BluetoothAdapter.getProfileProxy()

4.A2dpService.connect()

mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
           

發送一個藍牙連接配接的消息給A2dpStateMachine進行連接配接操作。a2dp連接配接之前肯定處于disconnect狀态,是以進行連接配接時要一步一個腳印。大家注意下面一段代碼是mDisconnected裡的。

switch(message.what) {
                case CONNECT:
                    BluetoothDevice device = (BluetoothDevice) message.obj;
                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
                                   BluetoothProfile.STATE_DISCONNECTED);----(1)

                    if (!connectA2dpNative(getByteAddress(device)) ) {-----(2)
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
                                       BluetoothProfile.STATE_CONNECTING);
                        break;
                    }

                    synchronized (A2dpStateMachine.this) {
                        mTargetDevice = device;
                        transitionTo(mPending);---------(3)
                    }
                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
                    //          sends back events consistently
                    sendMessageDelayed(CONNECT_TIMEOUT, 30000);
                    break;
           

        上面的(1)處是發送一個連接配接藍牙狀态改變事件。

                         (2)進行a2dp連接配接,很明顯就要進入jni裡了

                           (3)狀态轉換到==》pending==>connected

JNI

5.這裡隻講連接配接,是以下面要分析jni了

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) {
        ALOGE("Failed HF connection, status: %d", status);
    }
    env->ReleaseByteArrayElements(address, addr, 0);
    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
           

那麼這個sBluetoothA2dpInterface這個是什麼呢?其實這個就相當于java中對外提供的api接口,隻是在c中是頭檔案而已。

既然是藍牙肯定是在藍牙的頭檔案裡bt_av.h裡

typedef struct {

    /** set to sizeof(btav_interface_t) */
    size_t          size;
    /**
     * Register the BtAv callbacks
     */
    bt_status_t (*init)( btav_callbacks_t* callbacks );

    /** connect to headset */
    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );

    /** dis-connect from headset */
    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );

    /** Closes the interface. */
    void  (*cleanup)( void );
} btav_interface_t;
           

接下來就調用到./btif/src/btif_av.c

static const btav_interface_t bt_av_src_interface = {
    sizeof(btav_interface_t),
    init_src,
    src_connect_sink,
    disconnect,
    cleanup_src,
};

static const btav_interface_t bt_av_sink_interface = {
    sizeof(btav_interface_t),
    init_sink,
    sink_connect_src,
    disconnect,
    cleanup_sink,
};
           

至于a2dp的斷開,流程和連接配接類似,請大家自行分析。

上面有兩個connect到底使用哪一個呢?那就要看你的機器是發送連接配接還是接收連接配接了。就到這了,接下來的東西其實我沒搞太明白,就不誤導大家了。

如果感覺不錯,就贊一下。

藍牙(三)a2dp連接配接