天天看点

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

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) {

// TODO(BT) check return

mBluetoothHeadset.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,[java] view plaincopy

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的最前的代码注释。[java] view plaincopy

boolean connectAudio() {

// TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission

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));[java] view plaincopy

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,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了[java] view plaincopy

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,具体代码看下面[java] view plaincopy

// Try to initialize the profiles if they were not.

...........

// Reset the only-show-one-error-dialog tracking variable

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()方法的具体实现在[java] view plaincopy

public boolean connect(BluetoothDevice device) {

if (mService == null) return false;

List 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;就知道了[java] view plaincopy

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的具体实现了。[java] view plaincopy

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;