当前位置:   article > 正文

Android-蓝牙sco通话_蓝牙sco测试用例

蓝牙sco测试用例

APP调用AudioManager::startBluetoothSco()

  1. // frameworks/base/media/java/android/media/AudioManager.java
  2. public void startBluetoothSco(){
  3. service.startBluetoothSco(mICallBack,
  4. getContext().getApplicationInfo().targetSdkVersion);
  5. }
  6. // frameworks/base/services/core/java/com/android/server/audio/AudioService.java
  7. public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
  8. final int scoAudioMode =
  9. (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
  10. BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED;
  11. final String eventSource = new StringBuilder("startBluetoothSco()")
  12. .append(") from u/pid:").append(uid).append("/")
  13. .append(pid).toString();
  14. startBluetoothScoInt(cb, uid, pid, scoAudioMode, eventSource);
  15. }
  16. void startBluetoothScoInt(IBinder cb, int uid, int pid, int scoAudioMode, @NonNull String eventSource) {
  17. final long ident = Binder.clearCallingIdentity();
  18. mDeviceBroker.startBluetoothScoForClient(cb, uid, pid, scoAudioMode, eventSource);
  19. Binder.restoreCallingIdentity(ident);
  20. mmi.record();
  21. }
  22. // frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
  23. void startBluetoothScoForClient(IBinder cb, int uid, int pid, int scoAudioMode, @NonNull String eventSource) {
  24. AudioDeviceAttributes device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
  25. setCommunicationRouteForClient(cb, uid, pid, device, scoAudioMode, eventSource);
  26. }
  27. void setCommunicationRouteForClient(IBinder cb, int uid, int pid, AudioDeviceAttributes device, int scoAudioMode, String eventSource) {
  28. ......
  29. mBtHelper.startBluetoothSco(scoAudioMode, eventSource);
  30. ......
  31. }
  32. // frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
  33. synchronized boolean startBluetoothSco(int scoAudioMode, @NonNull String eventSource) {
  34. AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
  35. return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
  36. }
  37. private boolean requestScoState(int state, int scoAudioMode) {
  38. if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
  39. broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
  40. connectBluetoothScoAudioHelper(mBluetoothHeadset, mBluetoothHeadsetDevice, mScoAudioMode)
  41. ......
  42. }
  43. // 断开连接的时候发送断连的广播
  44. }

这里做了两件事,一个是发送广播,一个是调用connectBluetoothScoAudioHelper

先看广播发送

  1. private void broadcastScoConnectionState(int state) {
  2. mDeviceBroker.postBroadcastScoConnectionState(state);
  3. }
  4. // frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
  5. void postBroadcastScoConnectionState(int state) {
  6. sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
  7. }
  8. // 收到消息MSG_I_BROADCAST_BT_CONNECTION_STATE之后调用onBroadcastScoConnectionState
  9. // frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
  10. synchronized void onBroadcastScoConnectionState(int state) {
  11. Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
  12. newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
  13. newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
  14. mScoConnectionState);
  15. sendStickyBroadcastToAll(newIntent);
  16. mScoConnectionState = state;
  17. }
  18. private void sendStickyBroadcastToAll(Intent intent) {
  19. mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
  20. }
  21. // 最终这个广播发出去就被一个app接收去显示蓝牙俩呢及状态了。

再看connectBluetoothScoAudioHelper

  1. // frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
  2. private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) {
  3. switch (scoAudioMode) {
  4. case SCO_MODE_RAW:
  5. return bluetoothHeadset.connectAudio();
  6. case SCO_MODE_VIRTUAL_CALL:
  7. return bluetoothHeadset.startScoUsingVirtualVoiceCall();
  8. case SCO_MODE_VR:
  9. return bluetoothHeadset.startVoiceRecognition(device);
  10. default:
  11. return false;
  12. }
  13. }

Audio调用蓝牙的connectAudio

  1. // 这里也是各种client server交互,最终调用如下
  2. // packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
  3. boolean connectAudio(BluetoothDevice device) {
  4. final HeadsetStateMachine stateMachine = mStateMachines.get(device);
  5. stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
  6. return true;
  7. }
  8. // packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
  9. public boolean processMessage(Message message) {
  10. case CONNECT_AUDIO:
  11. mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); // 先把A2dpSuspended设置成true
  12. broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
  13. transitionTo(mAudioConnecting);
  14. }
  15. /**
  16. * A Bluetooth Handset StateMachine
  17. * (Disconnected)
  18. * | ^
  19. * CONNECT | | DISCONNECTED
  20. * V |
  21. * (Connecting) (Disconnecting)
  22. * | ^
  23. * CONNECTED | | DISCONNECT
  24. * V |
  25. * (Connected)
  26. * | ^
  27. * CONNECT_AUDIO | | AUDIO_DISCONNECTED
  28. * V |
  29. * (AudioConnecting) (AudioDiconnecting)
  30. * | ^
  31. * AUDIO_CONNECTED | | DISCONNECT_AUDIO
  32. * V |
  33. * (AudioOn)
  34. */
  35. // 接下来蓝牙就回进入audioon状态,进入audioon状态就会调用audio的setBluetoothScoOn
  36. (蓝牙app默认走高通的逻辑)
  37. class AudioOn extends ConnectedBase {
  38. public void enter() {
  39. ......
  40. mSystemInterface.getAudioManager().setBluetoothScoOn(true);
  41. ......
  42. }
  43. }

蓝牙进入audioon之后调用audio setBluetoothScoOn

这里需要注意,在蓝牙进入audio on 之前就已经先把a2dpsuspended设置成了true,先让a2dp停止,然后再进入audio on,然后再设置audio的setBluetoothScoOn。

接来下看看setBluetoothScoOn的处理:

  1. 蓝牙进入audioon模式就会调用mSystemInterface.getAudioManager().setBluetoothScoOn(true);
  2. // frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
  3. void setBluetoothScoOn(boolean on, String eventSource) {
  4. synchronized (mDeviceStateLock) {
  5. mBluetoothScoOn = on; // 这个标记在选择device的时候使用,如果是on就会选择sco设备
  6. sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource); // 这里就是更新route,选择设备
  7. }
  8. }
  9. // 收到MSG_L_UPDATE_COMMUNICATION_ROUTE信息之后就会调用onUpdateCommunicationRoute
  10. private void onUpdateCommunicationRoute(String eventSource) {
  11. AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice(); // 这里选择设备
  12. if (preferredCommunicationDevice == null || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
  13. AudioSystem.setParameters("BT_SCO=off");
  14. } else {
  15. AudioSystem.setParameters("BT_SCO=on");
  16. }
  17. postSetPreferredDevicesForStrategy(mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
  18. }
  19. private AudioDeviceAttributes preferredCommunicationDevice() {
  20. boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
  21. if (btSCoOn) {
  22. AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
  23. return device;
  24. }
  25. AudioDeviceAttributes device = requestedCommunicationDevice();
  26. return device;
  27. }

setBluetoothScoOn的作用就是: 1. 选择到sco device 2. 设置参数"BT_SCO=on"

Audio setparameter "A2dpSuspended=true" & "BT_SCO=on"

从目前的调用逻辑来看,在蓝牙app进入audioon状态之前就设置了A2dpSuspended=true,在蓝牙进入audioon之后告诉audio sco on 的时候audio侧设置了BT_SCO=on,所以这两个的先后顺序就是先设置"A2dpSuspended=true"再设置"BT_SCO=on"。

先看看设置"A2dpSuspended=true"

  1. // vendor/qcom/opensource/audio-hal/primary-hal/hal/AudioDevice.cpp
  2. int AudioDevice::SetParameters(const char *kvpairs) {
  3. ret = str_parms_get_str(parms, "A2dpSuspended" , value, sizeof(value));
  4. if (ret >= 0) {
  5. pal_param_bta2dp_t param_bt_a2dp;
  6. if (strncmp(value, "true", 4) == 0)
  7. param_bt_a2dp.a2dp_suspended = true;
  8. else
  9. param_bt_a2dp.a2dp_suspended = false;
  10. ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED /* PAL_PARAM_ID_BT_A2DP_SUSPENDED = 16*/, (void *)&param_bt_a2dp, sizeof(pal_param_bta2dp_t));
  11. }
  12. }
  13. pal_set_param 调用ResourceManager::setParameter -----> a2dp_dev->setDeviceParameter ---> rm->a2dpSuspend(); // 当suspend是true就调用rm->a2dpSuspend(), 是false就是rm->a2dpResume()

设置"BT_SCO=on"

  1. // vendor/qcom/opensource/audio-hal/primary-hal/hal/AudioDevice.cpp
  2. int AudioDevice::SetParameters(const char *kvpairs) {
  3. ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
  4. if (ret >= 0) {
  5. pal_param_btsco_t param_bt_sco;
  6. if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
  7. param_bt_sco.bt_sco_on = true;
  8. } else {
  9. param_bt_sco.bt_sco_on = false;
  10. }
  11. ret = pal_set_param(PAL_PARAM_ID_BT_SCO/*PAL_PARAM_ID_BT_SCO = 11*/, (void *)&param_bt_sco, sizeof(pal_param_btsco_t));
  12. }
  13. }
  14. pal_set_param 调用ResourceManager::setParameter
  15. // vendor/qcom/opensource/pal/resource_manager/src/ResourceManager.cpp
  16. int ResourceManager::setParameter(uint32_t param_id, void *param_payload, size_t payload_size) {
  17. case PAL_PARAM_ID_BT_SCO: {
  18. struct pal_device dattr;
  19. dattr.id = PAL_DEVICE_OUT_BLUETOOTH_SCO;
  20. dev = Device::getInstance(&dattr, rm); // 获取sco device
  21. param_bt_sco = (pal_param_btsco_t*)param_payload;
  22. status = dev->setDeviceParameter(param_id, param_payload);
  23. // 下面就是一些设备切换的逻辑
  24. std::vector <std::shared_ptr<Device>> rxDevices;
  25. std::vector <std::shared_ptr<Device>> txDevices;
  26. if (param_bt_sco->bt_sco_on == true) {
  27. for (auto& str : mActiveStreams) { // 循环处理所有stream
  28. str->getStreamAttributes(&sAttr);
  29. // 根据sAttr的type判断是否是rx且是需要切换到sco的stream如果是
  30. if(上面的条件满足) {
  31. str->getAssociatedDevices(associatedDevices);
  32. for (int i = 0; i < associatedDevices.size(); i++) {
  33. dAttr.id = (pal_device_id_t)associatedDevices[i]->getSndDeviceId();
  34. dev = Device::getInstance(&dAttr, rm);
  35. if (dev && (!isBtScoDevice(dAttr.id)) && (dAttr.id != PAL_DEVICE_OUT_PROXY) && isDeviceAvailable(PAL_DEVICE_OUT_BLUETOOTH_SCO)) {
  36. rxDevices.push_back(dev); // 把device放入需要切换的列表中
  37. 7823 }
  38. }
  39. } else if(如果是tx,且是需要切换到sco的stream) {
  40. 同上的方法,把需要切换到device放入txDevices列表中
  41. }
  42. }
  43. }
  44. sco_rx_dattr.id = PAL_DEVICE_OUT_BLUETOOTH_SCO;
  45. status = getDeviceConfig(&sco_rx_dattr, NULL);
  46. sco_tx_dattr.id = PAL_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
  47. status = getDeviceConfig(&sco_tx_dattr, NULL);
  48. SortAndUnique(rxDevices);
  49. SortAndUnique(txDevices);
  50. for (auto& device : rxDevices) {
  51. rm->forceDeviceSwitch(device, &sco_rx_dattr);
  52. }
  53. for (auto& device : txDevices) {
  54. rm->forceDeviceSwitch(device, &sco_tx_dattr);
  55. }
  56. }
  57. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/192208
推荐阅读
相关标签
  

闽ICP备14008679号