在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好的蓝牙这部分功能,不过本文的主题当然还是下面两个。
1.蓝牙耳机接听电话
这个就对应HFP(
Hands-freeProfile ),Free your Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,
void connectBluetoothAudio() { if (VDBG) log( "connectBluetoothAudio()..." ); if (mBluetoothHeadset != null ) { mBluetoothHeadset.connectAudio(); } mBluetoothConnectionPending = true ; mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
接下来就跳到蓝牙应用的管辖范围,代码在packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java,
public boolean connectAudio() { HeadsetService service = getService(); if (service == null ) return false ; return service.connectAudio(); }
很明显下一个目标是HeadsetService,直接看具体实现,这部分代码跳转都比较清晰,下面代码会先判断当前状态是否正确,关于HeadsetStateMachine几个状态可以参持这个/packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java的最前的代码注释。
boolean connectAudio() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission" ); if (!mStateMachine.isConnected()) { return false ; } if (mStateMachine.isAudioOn()) { return false ; } mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO); return true ; }
走进HeadsetStateMachine状态机,找到CONNECT_AUDIO分支,就看带Native的方法connectAudioNative(getByteAddress(mCurrentDevice));
static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) { jbyte *addr; bt_status_t status; if (!sBluetoothHfpInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) { ALOGE("Failed HF audio connection, status: %d" , status); } env->ReleaseByteArrayElements(address, addr, 0 ); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; }
上面代码还可以进一步跟到下面/external/bluetooth/bluedroid/btif/src/btif_hf.c,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了
static bt_status_t connect_audio( bt_bdaddr_t *bd_addr ) { CHECK_BTHF_INIT(); if (is_connected(bd_addr)) { BTA_AgAudioOpen(btif_hf_cb.handle); btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING, (char *)bd_addr, sizeof(bt_bdaddr_t), NULL); return BT_STATUS_SUCCESS; } return BT_STATUS_FAIL; }
2.在蓝牙列表中连接蓝牙耳机
A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了
DevicePickerFragment.java (settings\src\com\android\settings\bluetooth) 3884 2013 - 6 - 26 void onClicked() { int bondState = mCachedDevice.getBondState(); if (mCachedDevice.isConnected()) { askDisconnect(); } else if (bondState == BluetoothDevice.BOND_BONDED) { mCachedDevice.connect(true ); } ....... } void connect( boolean connectAllProfiles) { if (!ensurePaired()) { return ; } mConnectAttempted = SystemClock.elapsedRealtime(); connectWithoutResettingTimer(connectAllProfiles); }
代码路径这里packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具体代码看下面
........... mIsConnectingErrorPossible = true ; int preferredProfiles = 0 ; for (LocalBluetoothProfile profile : mProfiles) { if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) { if (profile.isPreferred(mDevice)) { ++preferredProfiles; connectInt(profile); } } } .............
connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在
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); }
下面是 BluetoothA2dp.java (frameworks\base\core\java\android\bluetooth) ,为什么是这样看下这个private BluetoothA2dp mService;就知道了
public boolean connect(BluetoothDevice 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 ; } }........... return false ; Binder跳转 public boolean connect(BluetoothDevice device) { A2dpService service = getService(); if (service == null ) return false ; return service.connect(device); } }
之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp的connectA2dpNative,同样到下面的代码,我们能看到的开放的代码也就是这些,再下面要看vendor的具体实现了。
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;
那到此为止,本文关于蓝牙耳机与蓝牙接听电话的流程分析也就结束了,同时蓝牙这一系列的文章也暂时结束,当然后续依然会关注蓝牙。本系列的第一篇文章标题是
入门 ,现在想想,这五篇文章下来也不过是刚刚入门而已,协议部分更是没怎么涉及呢,对于蓝牙BT需要深入研究的地方还有很多,仅希望这五篇文章可以帮你快速了解android蓝牙代码流程
转载自:http://blog.csdn.net/baimy1985/article/details/9275559