上一篇说了下A2DP的一些基本操作,这篇分析下系统应用、系统源码是如何操作A2DP的。尤其是其连接过程,基于Android4.3源码。Andorid手机一般都是做为A2DP Audio Source端。
媒体音频也就是A2DP,首先连接的蓝牙设备需要支持A2DP协议(并且做为A2DP Audio Sink端),并且需要与该设备进行配对,如何进行蓝牙配对这里就不细说了,可以参照我的其他文章。主要分析下其连接过程。
对于系统自带应用Settings中已配对的蓝牙设备界面(如下图所示):
其对应文件路径:
packages/apps/Settings/src/com/Android/settings/bluetooth/DeviceProfilesSettings.Java
点击媒体音频进行连接,调用onPreferenceChange。
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mDeviceNamePref) {
mCachedDevice.setName((String) newValue);
} else if (preference instanceof CheckBoxPreference) {
LocalBluetoothProfile prof = getProfileOf(preference); //获取对应的profile
onProfileClicked(prof, (CheckBoxPreference) preference);
return false;
} else {
return false;
}
return true;
}
接着看onProfileClicked()函数处理
private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) {
BluetoothDevice device = mCachedDevice.getDevice();
int status = profile.getConnectionStatus(device);
boolean isConnected =
status == BluetoothProfile.STATE_CONNECTED;
if (isConnected) {
askDisconnect(getActivity(), profile);
} else {
if (profile.isPreferred(device)) {
profile.setPreferred(device, false);
refreshProfilePreference(profilePref, profile);
} else {
profile.setPreferred(device, true);
mCachedDevice.connectProfile(profile);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
接着查看CachedBluetoothDevice中的connectProfile函数连接某一profile。
void connectProfile(LocalBluetoothProfile profile) {
mConnectAttempted = SystemClock.elapsedRealtime();
mIsConnectingErrorPossible = true;
connectInt(profile);
refresh();
}
synchronized void connectInt(LocalBluetoothProfile profile) {
if (!ensurePaired()) {
return;
}
if (profile.connect(mDevice)) {
return;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
connectProfile() ——>connectInt()
connectInt()函数中会先判断是否配对,如果没有配对则开始配对,配对成功后连接profile。
如果已经配对则直接连接profile。
对于profile.connect(mDevice)会根据profile调用各自对应的connect方法。(如手机音频则对应HeadsetProfile,媒体音频对应A2dpProfile)。这里查看手机音频的连接A2dpProfile。
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
List<BluetoothDevice> sinks = mService.getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
mService.disconnect(sink);
}
}
return mService.connect(device);
}
A2dpProfile.java中的connect()方法,mService是通过getProfileProxy获取的BluetoothA2DP代理对象,通过其进行A2DP相关操作。
mService.connect跳到Bluetooth应用中,
代码路径:packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java
先调用到内部类BluetoothA2dpBinder的connect方法。
public boolean connect(BluetoothDevice device) {
A2dpService service = getService();
if (service == null) return false;
return service.connect(device);
}
该方法中很明显是去调用A2dpService的connect方法。接着看A2dpService中的connect
public boolean connect(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
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;
}
A2dpService的connect()函数会对priority和连接状态进行必要的检查,不符合条件则返回false。符合条件则向状态机发送消息A2dpStateMachine.CONNECT。
此时A2dpStateMachine中状态应该是Disconnected,所以查看Disconnected state中的处理
BluetoothDevice device = (BluetoothDevice) message.obj;
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
if (!connectA2dpNative(getByteAddress(device)) ) {
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
break;
}
synchronized (A2dpStateMachine.this) {
mTargetDevice = device;
transitionTo(mPending);
}
sendMessageDelayed(CONNECT_TIMEOUT, 30000);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
A2DPStateMachine调用connectA2dpNative()函数来进行媒体音频的连接。connectA2dpNative是native方法,跳转到com_android_bluetooth_a2dp.cpp中,调用对应的方法connectA2dpNative
static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
jbyte *addr;
bt_bdaddr_t * btAddr;
bt_status_t status;
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) {
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
其中sBluetoothA2dpInterface->connect会跳到hardware、蓝牙协议栈进行连接,这就先不进行分析了。
当协议栈连接状态改变会回调com_android_bluetooth_a2dp.cpp中的方法bta2dp_connection_state_callback。
static void bta2dp_connection_state_callback(btav_connection_state_t state, bt_bdaddr_t* bd_addr) {
jbyteArray addr;
if (!checkCallbackThread()) { \
return; \
}
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint) state,
addr);
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
sCallbackEnv->DeleteLocalRef(addr);
}
bta2dp_connection_state_callback方法中会从cpp层调用到java层,对应于A2DPStateMachine中的onConnectionStateChanged函数
private void onConnectionStateChanged(int state, byte[] address) {
StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.valueInt = state;
event.device = getDevice(address);
sendMessage(STACK_EVENT, event);
}
onConnectionStateChanged函数中发送消息STACK_EVENT(携带状态和蓝牙地址),此时是Pending state,收到该消息调用processConnectionEvent。
正常连接成功应该会先收到CONNECTION_STATE_CONNECTING状态,然后收到CONNECTION_STATE_CONNECTED状态。
broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpStateMachine.this) {
mCurrentDevice = mTargetDevice;
mTargetDevice = null;
transitionTo(mConnected);
}
收到CONNECTION_STATE_CONNECTED状态,后向外发送连接成功的广播,状态机切换到Connected状态。
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState);
mWakeLock.acquire();
mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.
obtainMessage(MSG_CONNECTION_STATE_CHANGED,
prevState,newState,device),
delay);
}
broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。
欢迎扫一扫关注我的微信公众号,定期推送优质技术文章: