Android 蓝牙开发(十)A2DP源码分析
2017-06-08 00:51
691 查看
上一篇说了下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。
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
接着看onProfileClicked()函数处理
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。
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。
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
A2dpProfile.java中的connect()方法,mService是通过getProfileProxy获取的BluetoothA2DP代理对象,通过其进行A2DP相关操作。
mService.connect跳到Bluetooth应用中,
代码路径:packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java
先调用到内部类BluetoothA2dpBinder的connect方法。
2
3
4
5
1
2
3
4
5
该方法中很明显是去调用A2dpService的connect方法。接着看A2dpService中的connect
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
A2dpService的connect()函数会对priority和连接状态进行必要的检查,不符合条件则返回false。符合条件则向状态机发送消息A2dpStateMachine.CONNECT。
此时A2dpStateMachine中状态应该是Disconnected,所以查看Disconnected state中的处理
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
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。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bta2dp_connection_state_callback方法中会从cpp层调用到java层,对应于A2DPStateMachine中的onConnectionStateChanged函数
2
3
4
5
6
1
2
3
4
5
6
onConnectionStateChanged函数中发送消息STACK_EVENT(携带状态和蓝牙地址),此时是Pending state,收到该消息调用processConnectionEvent。
正常连接成功应该会先收到CONNECTION_STATE_CONNECTING状态,然后收到CONNECTION_STATE_CONNECTED状态。
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
收到CONNECTION_STATE_CONNECTED状态,后向外发送连接成功的广播,状态机切换到Connected状态。
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。
欢迎扫一扫关注我的微信公众号,定期推送优质技术文章:
1 连接过程 |
对于系统自带应用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) {//check box LocalBluetoothProfile prof = getProfileOf(preference); //获取对应的profile onProfileClicked(prof, (CheckBoxPreference) preference); return false; // checkbox will update from onDeviceAttributesChanged() callback } else { return false; } return true; }1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
接着看onProfileClicked()函数处理
private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) { BluetoothDevice device = mCachedDevice.getDevice(); //获取配对的蓝牙设备 int status = profile.getConnectionStatus(device); //获取profile的连接状态 boolean isConnected = status == BluetoothProfile.STATE_CONNECTED; if (isConnected) { //如果是连接状态则断开连接 askDisconnect(getActivity(), profile); } else { //没有连接 if (profile.isPreferred(device)) { //获取profile是否是首选 // profile is preferred but not connected: disable auto-connect profile.setPreferred(device, false); //设置对应profile的PRIORITY 为off,防止自动连接 refreshProfilePreference( 4000 profilePref, profile); //刷新check box状态 } else { profile.setPreferred(device, true); //设置对应profile的PRIORITY 为on mCachedDevice.connectProfile(profile); //连接指定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(); // Reset the only-show-one-error-dialog tracking variable mIsConnectingErrorPossible = true; connectInt(profile); //连接profile refresh(); // 刷新ui } 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; //获取连接hfp的设备 List<BluetoothDevice> sinks = mService.getConnectedDevices(); if (sinks != null) { for (BluetoothDevice sink : sinks) { mService.disconnect(sink); //断开连接 } } //连接hfp。 return mService.connect(device); }1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
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); }1
2
3
4
5
1
2
3
4
5
该方法中很明显是去调用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; //检查priority } int connectionState = mStateMachine.getConnectionState(device); if (connectionState == BluetoothProfile.STATE_CONNECTED || connectionState == BluetoothProfile.STATE_CONNECTING) { return false; //检查连接状态 } mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); return true; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
A2dpService的connect()函数会对priority和连接状态进行必要的检查,不符合条件则返回false。符合条件则向状态机发送消息A2dpStateMachine.CONNECT。
此时A2dpStateMachine中状态应该是Disconnected,所以查看Disconnected state中的处理
BluetoothDevice device = (BluetoothDevice) me 11f68 ssage.obj; //发送广播,正在连接A2DP 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; //mTargetDevice要连接的设备 transitionTo(mPending); //切换到pending状态 } //超时处理 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、蓝牙协议栈进行连接,这就先不进行分析了。
2 状态回调 |
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); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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); }1
2
3
4
5
6
1
2
3
4
5
6
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;//mCurrentDevice表示已连接的设备 mTargetDevice = null; //mTargetDevice表示要连接的设备 transitionTo(mConnected);//切换到Connected状态 }1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
收到CONNECTION_STATE_CONNECTED状态,后向外发送连接成功的广播,状态机切换到Connected状态。
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { //AudioManager设置A2DP的连接状态 int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState); mWakeLock.acquire(); //延时处理,发送广播 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler. obtainMessage(MSG_CONNECTION_STATE_CHANGED, prevState,newState,device), delay); }1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
broadcastConnectionState中会向AudioManager中设置A2DP的连接状态,返回值用来延时发送广播。AudioManager设置A2DP的连接状态非常重要,这样音频系统根据当前状态,判断音频从哪里发出(蓝牙a2dp、扬声器、耳机)。
欢迎扫一扫关注我的微信公众号,定期推送优质技术文章:
相关文章推荐
- Android 蓝牙开发(十)A2DP源码分析
- Android蓝牙开发【十】A2DP源码分析
- Android 蓝牙开发(十)A2DP源码分析
- Android 蓝牙开发(十)A2DP源码分析
- Android 蓝牙开发(十)A2DP源码分析
- Android菜鸟开发之蓝牙(Bluetooth)---设置应用源码分析
- Android底层开发之旅—蓝牙系统分析
- [Android源码分析]蓝牙打开分析--苦尽甘来之再次回到jni之上
- Android上的蓝牙通信功能的开发:BluetoothChat例程分析
- [Android源码分析]蓝牙文件传输过程解析之UI实现
- Android上的蓝牙通信功能的开发:BluetoothChat例程分析
- [Android源码分析]蓝牙打开流程分析——jni层之下的偷偷摸摸(Service Record的创建)
- Android开发——MediaPlayer源码不完整分析
- Android开发——MediaPlayer源码不完整分析
- [Android源码解析]蓝牙扫描结果反馈的分析
- 蓝牙文件传输之obex层之上的分析【Android源码解析】
- [Android源码分析]蓝牙打开流程分析——jni层之上的方方面面
- Android上蓝牙通信功能开发:BluetoothChat例程分析
- Android底层开发之旅—蓝牙系统分析
- Android Launcher开发(六)通过分析Laucher源码整理添加快捷方式的思路