Android蓝牙开发【七】hfp音频链接

接着上一篇hfp链接继续,查看蓝牙通话时如何进行处理的。hfp链接有两个链接,一个是hfp链接(在设置界面显示的是手机音频),另外一个是蓝牙通话时进行的音频链接。这篇说下第二个链接,音频链接处理过程。 java

该文章是基于Android源码4.3的android

 

 

1 链接音频

 

在手机音频正常链接时,接通电话,并选择蓝牙通话。从系统应用Phone开始分析。 
代码路径:packages/apps/Phone/src/com/Android/phone/InCallScreen.Java 
手机通话能够选择扬声器、听筒、蓝牙,咱们选择蓝牙。 
这里写图片描述数组

 
  1. public void switchInCallAudio(InCallAudioMode newMode) {app

  2. switch (newMode) {函数

  3. case SPEAKER: break; //扬声器 ui

  4. case BLUETOOTH: //蓝牙this

  5. // 检查hfp是否链接着(蓝牙耳机是否链接可用),检查蓝牙耳机的音频是否链接spa

  6. if (isBluetoothAvailable() && !isBluetoothAudioConnected()) {线程

  7. if (PhoneUtils.isSpeakerOn(this)) { //关闭扬声器代理

  8. PhoneUtils.turnOnSpeaker(this, false, true);

  9. }

  10. connectBluetoothAudio(); //链接蓝牙音频

  11. }

  12. break;

  13. case EARPIECE:break; //听筒

  14. default: break;

  15. }

  16. updateInCallTouchUi(); //更新ui

  17. }


蓝牙通话时选择蓝牙,会调到switchInCallAudio(),对于蓝牙通话模式,检查是否链接蓝牙耳机 headset(手机音频),检查蓝牙通话音频是否链接,若是有链接的蓝牙耳机,而且没有链接蓝牙音频(这个链接并非设置界面中的手机音频链接,这是通话是须要的链接,该链接的前提是须要进行手机音频的链接),则知足条件。 
若是扬声器开着,则先关闭扬声器,而后链接蓝牙音频。接着看connectBluetoothAudio()函数。

 

 
/* package */ void connectBluetoothAudio() {/* package */ void connectBluetoothAudio() {


if (mBluetoothHeadset != null) { mBluetoothHeadset.connectAudio(); } //注意:蓝牙链接不会当即发生;connectAudio()调用当即返回,但实际它在另外一个线程中工做。 //mBluetoothConnectionPending标志只是一个标志,以确保屏幕UI当即更新。 mBluetoothConnectionPending = true; mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();}

 


mBluetoothHeadset是经过getProfileProxy获取的BluetoothHeadset代理对象。经过代理对象链接音频。mBluetoothHeadset.connectAudio()会跳到应用Settings中HeadsetService内部类BluetoothHeadsetBinder中的connectAudio()方法,而后又跳到HeadsetService的connectAudio()函数中。 
HeadsetService的connectAudio()函数以下:

 

 

 
  1. boolean connectAudio() {

  2. enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

  3. if (!mStateMachine.isConnected()) { //检查手机音频是否链接

  4. return false;

  5. }

  6. if (mStateMachine.isAudioOn()) { //检查音频是否链接

  7. return false;

  8. } //向状态机发送消息

  9. mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);

  10. return true;

  11. }

在HeadsetService的connectAudio()函数中检查headset是否链接,音频是否链接。向状态机发送链接音频的消息。此时headset是链接的,HeadsetStateMachine中的状态是Connected。 
接收到后CONNECT_AUDIO的消息进行以下处理:

 

 
  1. //mCurrentDevice表示状态改变前链接的设备。

  2. connectAudioNative(getByteAddress(mCurrentDevice));

mCurrentDevice表示状态改变前链接的设备。经过getByteAddress获取该设备的蓝牙地址。而后调用native方法connectAudioNative链接音频,该方法会调用jni目录下的 
com_android_bluetooth_hfp.cpp中的connectAudioNative函数。

 

 
  1. static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {

  2. jbyte *addr;

  3. bt_status_t status;

  4. if (!sBluetoothHfpInterface) return JNI_FALSE;

  5. //将byte数组类型的地址转换为jbyte*类型

  6. addr = env->GetByteArrayElements(address, NULL);

  7. if (!addr) {

  8. jniThrowIOException(env, EINVAL);

  9. return JNI_FALSE;

  10. }

  11. //链接audio

  12. if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=

  13. BT_STATUS_SUCCESS) {

  14. }

  15. env->ReleaseByteArrayElements(address, addr, 0);

  16. return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;

  17. }

将byte数组类型的地址转换成jbyte*类型,而后向hardware、协议栈下进行链接。


 

2 音频链接状态

当音频链接状态改变会回调com_android_bluetooth_hfp.cpp中audio_state_callback函数。 
audio_state_callback函数以下:

 
  1. static void audio_state_callback(bthf_audio_state_t state, bt_bdaddr_t* bd_addr) {

  2. jbyteArray addr;

  3.  
  4. CHECK_CALLBACK_ENV

  5. //获取蓝牙地址

  6. addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));

  7. if (!addr) {

  8. checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

  9. return;

  10. }

  11. sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);

  12. //调用method_onAudioStateChanged对应的方法。

  13. sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state, addr);

  14. checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

  15. sCallbackEnv->DeleteLocalRef(addr);

  16. }

audio_state_callback中参数state表示音频链接状态,address表示蓝牙的地址。将address转换为jbyteArray类型,而后调用java层代码,调用HeadSetStateMachine中的onAudioStateChanged函数。onAudioStateChanged代码以下:

 
  1. private void onAudioStateChanged(int state, byte[] address) {

  2. StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);

  3. event.valueInt = state;

  4. event.device = getDevice(address);

  5. sendMessage(STACK_EVENT, event); //发送消息

  6. }



onAudioStateChanged向状态机发送消息。此时状态机处于Connected状态,收到该消息调用processAudioEvent(event.valueInt, event.device)函数。processAudioEvent代码以下:

 
  1. private void processAudioEvent(int state, BluetoothDevice device) {

  2. if (!mCurrentDevice.equals(device)) { //查看是不是以前链接的设备

  3. return;

  4. }

  5. switch (state) {

  6. case HeadsetHalConstants.AUDIO_STATE_CONNECTED:

  7. mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;

  8. //设置蓝牙SCO进行通讯。

  9. mAudioManager.setBluetoothScoOn(true);

  10. broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,

  11. BluetoothHeadset.STATE_AUDIO_CONNECTING);

  12. transitionTo(mAudioOn); //切换到AudioOn状态

  13. break;

  14. case HeadsetHalConstants.AUDIO_STATE_CONNECTING:

  15. mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;

  16. broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,

  17. BluetoothHeadset.STATE_AUDIO_DISCONNECTED);

  18. break;

  19. default:

  20. break;

  21. }

  22. }

 

音频链接回调,状态是HeadsetHalConstants.AUDIO_STATE_CONNECTING或HeadsetHalConstants.AUDIO_STATE_CONNECTED,向外发送audio链接状态改变的广播。状态是HeadsetHalConstants.AUDIO_STATE_CONNECTED,经过AudioManager设置蓝牙SCO进行音频通讯,将状态机切换到AudioOn状态。

能够经过广播接收者注册BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,监听音频到链接状态的改变。


 

3 音频断开链接

蓝牙通话状态下,切换到听筒、扬声器或者中止通话,都会将音频断开链接。在应用Phone中的InCallScreen.java中调用disconnectBluetoothAudio,代码以下:

 
  1. /* package */ void disconnectBluetoothAudio() {

  2. if (mBluetoothHeadset != null) {

  3. //断开音频链接

  4. mBluetoothHeadset.disconnectAudio();

  5. }

  6. mBluetoothConnectionPending = false;

  7. }

mBluetoothHeadset.disconnectAudio()经过代理对象调用disconnectAudio(),跳转到应用Bluetooth的HeadSetService内部类BluetoothHeadsetBinder中的disconnectAudio()中,而后跳到HeadSetService的disconnectAudio()函数中。

 
  1. boolean disconnectAudio() {

  2. enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

  3. //判断状态机状态是否处于AudioOn状态

  4. if (!mStateMachine.isAudioOn()) {

  5. return false;

  6. } //发送DISCONNECT_AUDIO消息

  7. mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);

  8. return true;

  9. }

此时HeadsetStateMachine状态为AudioOn,接收到消息后处理以下:

 
  1. ase DISCONNECT_AUDIO:

  2. if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {

  3. mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;

  4. //音频管理关闭蓝牙SCO。

  5. mAudioManager.setBluetoothScoOn(false);

  6. //发送广播

  7. broadcastAudioState(mCurrentDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,

  8. BluetoothHeadset.STATE_AUDIO_CONNECTED);

  9. }

  10. break;

 

disconnectAudioNative为native方法,调用到jni关闭音频链接。关闭蓝牙SCO耳机通信,向外发送广播并向蓝牙耳机发送通话状态。

相关文章
相关标签/搜索