蓝牙 bluetooth (五)接电话与听音乐

前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙HFP/A2DP的profile,由于这部分也算是蓝牙的经典功能,所以代码流程并不是很复杂,当然不复杂仅是对于代码调用流程而言,对于HFP/A2DP协议相关的东东还没有精力去看,其难易程序也无法评价。下面从两个点HFP与A2DP来展开本文的代码跟踪:

正文开始之前,先说点题外话,在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好的蓝牙这部分功能,不过本文的主题当然还是下面两个。

1.蓝牙耳机接听电话

这个就对应HFP(Hands-freeProfile),Free your Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,

/* package */ void connectBluetoothAudio() {if (VDBG) log("connectBluetoothAudio()…");if (mBluetoothHeadset != null) {// TODO(BT) check returnmBluetoothHeadset.connectAudio();}// Watch out: The bluetooth connection doesn’t happen instantly;// the connectAudio() call returns instantly but does its real// work in another thread. The mBluetoothConnectionPending flag// is just a little trickery to ensure that the onscreen UI updates// instantly. (See isBluetoothAudioConnectedOrPending() above.)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() {// TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permissionenforceCallingOrSelfPermission(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);/* Inform the application that the audio connection has been initiated successfully */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)38842013-6-26void 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,具体代码看下面

// Try to initialize the profiles if they were not…………// Reset the only-show-one-error-dialog tracking variablemIsConnectingErrorPossible = 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()方法的具体实现在

沿途跟着一条河,你看着它在晨光暮霭中变换着色彩,

蓝牙 bluetooth (五)接电话与听音乐

相关文章:

你感兴趣的文章:

标签云: