蓝牙配对及A2DP连接

  当点击设置界面的某个蓝牙设备时会调用DeviceListPreferenceFragment里面:

android 利用蓝牙拉起 安卓设置蓝牙_2d


  最终调用BluetoothDevicePreference.onClicked(),主要代码如下:

android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_02


  上面的逻辑里会根据不同的情况执行不同的逻辑,一目了然。

蓝牙配对

  调用CachedBluetoothDevice.startPairing()

android 利用蓝牙拉起 安卓设置蓝牙_2d_03


  配对之前,如果当前处于蓝牙扫描状态,则停止蓝牙扫描。然后调用createBond()开始绑定(即配对)了。

  BluetoothDevice.createBond()>adapterservice.createBond()>adapterservice.createBond()>向状态机发送绑定消息》  BondStateMachine.createBond()>AdapterService.createBondNative()>JNI==>hw,

android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_04

蓝牙连接

  当配对成功之后,会发送BONDING_STATE_CHANGE消息到BondStateMachine:

  BondStateMachine::PendingCommandState.processMessage()

android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_05


  调用BondStateMachine::PendingCommandState.sendIntent()发ACTION_BOND_STATE_CHANGED消息给应用软件:

android 利用蓝牙拉起 安卓设置蓝牙_2d_06


  上面先调用了AdapterProperties.onBondStateChanged(),其中调用setBondState更新远程设备的Bond状态。

  ACTION_BOND_STATE_CHANGED消息给Setting软件的BluetoothEventManager处理,比如接收到bond完成事件BondStateChangedHandler,里面调用了

cachedDevice.onBondingStateChanged(bondState);

android 利用蓝牙拉起 安卓设置蓝牙_状态机_07


  这里省略一些逻辑,之后调用到connectAutoConnectableProfiles()

android 利用蓝牙拉起 安卓设置蓝牙_2d_08


  for循环遍历了所以支持的蓝牙协议,如果该协议允许自动连接,则尝试connect。

android 利用蓝牙拉起 安卓设置蓝牙_android_09


  连接成功会有以下打印:

android 利用蓝牙拉起 安卓设置蓝牙_android_10


这里了解一下A2dpProfile.connect()

android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_11


  先断开已经连接的A2DP远程设备,再连接指定的设备。

A2dpProfile.connect()–>BluetoothA2dp.connect()–>A2dpService.connect() 发送A2dpStateMachine.CONNECT给状态机–>A2dpStateMachine.Disconnected.processMessage() -->connectA2dpNative(),连接超时时间是30秒。

android 利用蓝牙拉起 安卓设置蓝牙_状态机_12


android 利用蓝牙拉起 安卓设置蓝牙_状态机_13


  上面代码

  (1)处是发送一个连接蓝牙状态改变事件。

  (2)进行a2dp连接,很明显就要进入jni里了

  (3)把要连接的远程蓝牙设备设置为目标设备mTargetDevice,状态转换到==》pending==>connected

这里只讲连接,所以下面要分析jni了。com_android_bluetooth_a2dp.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) {
        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,此处省略。
  

  蓝牙A2DP连接成功,bluedroid回调com_android_bluetooth_a2dp.cpp bta2dp_connection_state_callback(),然后回调A2dpStateMachine.onConnectionStateChanged()

android 利用蓝牙拉起 安卓设置蓝牙_状态机_14


  发送STACK_EVENT消息,因当前状态是Pending,所以调用Pending.processMessage():

android 利用蓝牙拉起 安卓设置蓝牙_状态机_15


processConnectionEvent()

android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_16


android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_17


发送MSG_CONNECTION_STATE_CHANGED消息,留意下参数内容。

android 利用蓝牙拉起 安卓设置蓝牙_状态机_18


  发送BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED广播,并把状态信息传递过去。另外会调用notifyProfileConnectionStateChanged(),修改Profile的优先级等(因为蓝牙带宽是有限的)。

  在LocalBluetoothProfileManager.updateLocalProfiles()有注册接收BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED广播,由StateChangedHandler类处理广播:

android 利用蓝牙拉起 安卓设置蓝牙_2d_19


android 利用蓝牙拉起 安卓设置蓝牙_状态机_20


android 利用蓝牙拉起 安卓设置蓝牙_android 利用蓝牙拉起_21


  cachedDevice.refresh()–>dispatchAttributesChanged()–>BluetoothDevicePreference.onDeviceAttributesChanged(),更新了UI界面关于蓝牙远程设备对应的Prcference。