Android startBluetoothSco не запускает sco, но isBluetoothScoOn возвращает true

Я создал репозиторий GitHub с образцом проекта, который показывает ниже проблему, о которой я спрашиваю здесь:
https://github.com/paulpv/audio-loopback/tree/simplified/src/com/twistpair/wave/experimental/loopback
(пожалуйста, прикрепите к "упрощенной" ветке и не обращайте внимания на "ведущую" ветку)

Два основных файла:

Отказ от ответственности: В настоящее время я использую только один Samsung Epic SPH-D700, работающий с CyanogenMod 10 Jelly Bean для кодирования и тестирования этого. Я не пробовал это на других устройствах, но, возможно, это может помешать мне вытащить мои волосы и сходить с ума.

Я боролся с тем, чтобы Android Bluetooth SCO был надежно запускать и останавливать и захватывать/воспроизводить аудио ДЛЯ МЕСЯЦ!
Как только я могу подключить телефон к режиму SCO, захват и воспроизведение через AudioRecord и AudioTrack (соответственно) отлично работают, как описано.
Проблема, с которой я сталкиваюсь, заключается в том, что я не могу надежно подключить телефон к режиму SCO!

Примеры в "Интернетах" для использования startBluetoothSco() и setBluetoothScoOn (true) кажутся простыми и прямыми, но когда я использую их на своем устройстве, они почти никогда не работают надежно. Я создал собственное тестовое приложение, которое ничего не делает, кроме запуска и остановки SCO, и я даже не могу заставить это работать с надежностью!

Мой код прослушивает BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED EXTRA_STATE==CONNECTED.
Я могу надежно обнаружить, когда какая-либо гарнитура подключена или отключена.

При обнаружении соединения мой обработчик немедленно вызывает startBluetoothSco().
A может поклясться, что по крайней мере однажды это пнуло SCO_AUDIO_STATE до C O NNECTED, но в 99% случаев это просто приводит к переходу от DISCONNECTED->CONNECTING->DISCONNECTED.

Вот мой аннотированный вывод журнала из моего примера приложения GitHub:

10-03 17:00:13.970: I/dalvikvm(29487): Debugger is active
10-03 17:00:14.158: I/System.out(29487): Debugger has connected
10-03 17:00:15.779: I/System.out(29487): waiting for debugger to settle...
10-03 17:00:15.978: I/System.out(29487): debugger has settled (1325)

My app начинает с гарнитуры Jawbone выключен и обновляет интерфейс...

10-03 17:00:16.568: D/MainActivity(29487): updateScreen()...
10-03 17:00:16.572: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false

... обновление пользовательского интерфейса
Приличная трансляция, которая сообщает мне текущий SCO_AUDIO_STATE...

10-03 17:00:16.689: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
10-03 17:00:16.689: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2, "android.media.extra.SCO_AUDIO_STATE"=0}
10-03 17:00:16.689: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
10-03 17:00:16.693: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
10-03 17:00:16.693: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_DISCONNECTED(0)
10-03 17:00:16.693: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_DISCONNECTED

... текущий SCO_AUDIO_STATE == DISCONNECTED; ожидается, так как моя гарнитура отключена.
Мой SCO Disconnected прослушиватель событий вызывается и обновляет UI w/two sendMessages...

10-03 17:00:16.693: I/MainActivity(29487): onAudioManagerScoAudioDisconnected()
10-03 17:00:16.755: D/libEGL(29487): loaded /vendor/lib/egl/libEGL_POWERVR_SGX540_120.so
10-03 17:00:16.787: D/libEGL(29487): loaded /vendor/lib/egl/libGLESv1_CM_POWERVR_SGX540_120.so
10-03 17:00:16.791: D/libEGL(29487): loaded /vendor/lib/egl/libGLESv2_POWERVR_SGX540_120.so
10-03 17:00:16.888: D/OpenGLRenderer(29487): Enabling debug mode 0
10-03 17:00:16.912: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
10-03 17:00:16.912: D/MainActivity(29487): updateScreen()...
10-03 17:00:16.912: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
10-03 17:00:16.927: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
10-03 17:00:16.927: D/MainActivity(29487): updateScreen()...
10-03 17:00:16.931: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false

... обновление пользовательского интерфейса

Через ~ 20 секунд я включаю гарнитуру Jawbone...

10-03 17:00:37.572: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED flg=0x10 (has extras) }
10-03 17:00:37.583: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=0, "android.bluetooth.profile.extra.STATE"=1}
10-03 17:00:37.587: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothHeadsetStatePrevious=STATE_DISCONNECTED(0)
10-03 17:00:37.587: D/AudioStateManager(29487): ==> bluetoothHeadsetState=STATE_CONNECTING(1)
10-03 17:00:37.619: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED flg=0x10 (has extras) }
10-03 17:00:37.623: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=1, "android.bluetooth.profile.extra.STATE"=2}
10-03 17:00:37.623: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
10-03 17:00:37.623: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
10-03 17:00:37.626: D/AudioStateManager(29487): ==> bluetoothHeadsetStatePrevious=STATE_CONNECTING(1)
10-03 17:00:37.626: D/AudioStateManager(29487): ==> bluetoothHeadsetState=STATE_CONNECTED(2)

Связанная челюсть; Мой прослушиватель событий называется...

10-03 17:00:37.626: I/MainActivity(29487): onBluetoothHeadsetConnected()

... видит, что мы можем SCO...

10-03 17:00:37.626: D/AudioStateManager(29487): mAudioManager.isBluetoothScoAvailableOffCall()=true

... и автовызов startBluetoothSco()
ЗДЕСЬ ГДЕ ПРОБЛЕМА! Почему этот вызов для запускаBluetoothSco не приводит к SCO_AUDIO_STATE == CONNECTED?!?!

10-03 17:00:37.626: D/AudioStateManager(29487): startBluetoothSco()
10-03 17:00:37.626: I/AudioStateManager(29487): mAudioManager.startBluetoothSco();

Мой прослушиватель событий заканчивает работу с sendMessage для обновления пользовательского интерфейса с текущим состоянием BT...

10-03 17:00:37.646: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
10-03 17:00:37.650: D/MainActivity(29487): updateScreen()...
10-03 17:00:37.650: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false

... обновление пользовательского интерфейса
Первый результат от startBluetoothSco входит в...

10-03 17:00:37.681: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
10-03 17:00:37.681: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=2, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=0}
10-03 17:00:37.681: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
10-03 17:00:37.685: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_DISCONNECTED(0)
10-03 17:00:37.685: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_CONNECTING(2)

... перешел из DISCONNECTED в CONNECTING
Второй результат от startBluetoothSco входит в...

10-03 17:00:37.759: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
10-03 17:00:37.763: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=0, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2}
10-03 17:00:37.763: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
10-03 17:00:37.763: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
10-03 17:00:37.763: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_DISCONNECTED(0)
10-03 17:00:37.763: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_DISCONNECTED

... переместился из раздела CONNECTING в ОТКЛЮЧЕНО
Я бы ожидал, что ШОС перейдет от ПОДКЛЮЧЕНИЯ к ПОДКЛЮЧЕНА!
Вызывается мой прослушиватель событий и обновляет UI w/two sendMessages...

10-03 17:00:37.763: I/MainActivity(29487): onAudioManagerScoAudioDisconnected()
10-03 17:00:37.767: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
10-03 17:00:37.767: D/MainActivity(29487): updateScreen()...
10-03 17:00:37.767: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false
10-03 17:00:37.783: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
10-03 17:00:37.783: D/MainActivity(29487): updateScreen()...
10-03 17:00:37.783: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=false

... обновление пользовательского интерфейса

Я жду ~ 20 секунд для подключения SCO, но он никогда не приходит. Я нажимаю кнопку "startBluetoothSco" моего приложения.
ЗАМЕЧАНИЕ, ЧТО ЭТО РЕЗУЛЬТАТЫ В ТОЧНОМ ТОЧНОМ ЗВОНОК запускатьBluetoothSco() AT 17: 00: 37.626

10-03 17:01:01.689: D/AudioStateManager(29487): startBluetoothSco()
10-03 17:01:01.689: I/AudioStateManager(29487): mAudioManager.startBluetoothSco();

Первый результат от startBluetoothSco входит...

10-03 17:01:01.708: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
10-03 17:01:01.712: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=2, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=0}
10-03 17:01:01.712: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
10-03 17:01:01.712: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_DISCONNECTED(0)
10-03 17:01:01.712: D/AudioStateManager(29487): ==> scoAudioState=SCO_AUDIO_STATE_CONNECTING(2)

... перешел из DISCONNECTED в CONNECTING
Здесь ситуация отличается от автоматического вызова startBluetoothSco() в 17:00: 37.626
Мы получаем событие BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED...

10-03 17:01:01.716: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED flg=0x10 (has extras) }
10-03 17:01:01.720: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=10, "android.bluetooth.profile.extra.STATE"=11}
10-03 17:01:01.720: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioStatePrevious=STATE_AUDIO_DISCONNECTED(10)
10-03 17:01:01.720: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioState=STATE_AUDIO_CONNECTING(11)

... перешел из DISCONNECTED в CONNECTING
Мы получили еще одно событие BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED...

10-03 17:01:02.572: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED flg=0x10 (has extras) }
10-03 17:01:02.576: D/AudioStateManager(29487): extras={"android.bluetooth.device.extra.DEVICE"=00:21:3C:00:3E:02, "android.bluetooth.profile.extra.PREVIOUS_STATE"=11, "android.bluetooth.profile.extra.STATE"=12}
10-03 17:01:02.576: D/AudioStateManager(29487): mReceiver: BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED
10-03 17:01:02.576: D/AudioStateManager(29487): ==> bluetoothDevice=00:21:3C:00:3E:02
10-03 17:01:02.576: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioStatePrevious=STATE_AUDIO_CONNECTING(11)
10-03 17:01:02.580: D/AudioStateManager(29487): ==> bluetoothHeadsetAudioState=STATE_AUDIO_CONNECTED(12)

... перешел из раздела CONNECTING to CONNECTED
Событие обновляет пользовательский интерфейс с одним сообщением SendMessage

10-03 17:01:02.580: I/MainActivity(29487): onBluetoothHeadsetAudioConnected()
10-03 17:01:02.580: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
10-03 17:01:02.580: D/MainActivity(29487): updateScreen()...
10-03 17:01:02.583: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true

... Выполнено обновление пользовательского интерфейса (честно говоря, я не уверен, что вызывается isBluetoothScoOn во второй раз)

10-03 17:01:02.603: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true

Второй результат от startBluetoothSco входит...

10-03 17:01:02.603: D/AudioStateManager(29487): onReceive: intent=Intent { act=android.media.ACTION_SCO_AUDIO_STATE_UPDATED flg=0x10 (has extras) }
10-03 17:01:02.607: D/AudioStateManager(29487): extras={"android.media.extra.SCO_AUDIO_STATE"=1, "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"=2}
10-03 17:01:02.607: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED
10-03 17:01:02.607: D/AudioStateManager(29487): ==> scoAudioStatePrevious=SCO_AUDIO_STATE_CONNECTING(2)
10-03 17:01:02.607: D/AudioStateManager(29487): ==> scoAudioState=.SCO_AUDIO_STATE_CONNECTED(1)
10-03 17:01:02.607: D/AudioStateManager(29487): android.media.ACTION_SCO_AUDIO_STATE_UPDATED: SCO_AUDIO_STATE_CONNECTED

... переместился из CONNECTING в CONNECTED

НАКОНЕЦ!
Вызывается мой прослушиватель событий и обновляет UI w/two sendMessages...

10-03 17:01:02.611: I/MainActivity(29487): onAudioManagerScoAudioConnected()
10-03 17:01:02.630: D/MainActivity(29487): MSG_UPDATE_BLUETOOTH_INDICATION
10-03 17:01:02.630: D/MainActivity(29487): updateScreen()...
10-03 17:01:02.634: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
10-03 17:01:02.650: D/MainActivity(29487): MSG_UPDATE_AUDIO_OUTPUT_STREAM_TYPE
10-03 17:01:02.650: D/MainActivity(29487): updateScreen()...
10-03 17:01:02.650: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true

... обновление пользовательского интерфейса

Все (на этот раз) работает, когда я "вручную" запускаю SCO через некоторое время, но не, если я автоматически запускаю SCO сразу после подключения гарнитуры.

Чтобы ухудшить ситуацию, когда ситуация не работает должным образом, я вижу странное поведение в состояниях ШОС:

  • Откладывание startBluetoothSco() для резонансных 3-5 секунд, похоже, не имеет значения. Я не пробовал откладывать его более 5 секунд. Ожидание более 5 секунд, когда звук начнет поступать на вашу гарнитуру BT, смешно.
  • Иногда вызов isBluetoothScoOn() возвращает true, даже когда я никогда не получал широковещательное событие с момента последнего состояния DISCONNECTED, заявляющего, что состояние изменено на CONNECTED.
  • Иногда "вручную" вызов startBluetoothSco() из пользовательского интерфейса ничего не делает, как будто SCO уже включен, но я никогда не получал никакого широковещательного события с момента последнего состояния DISCONNECTED, заявляющего, что состояние изменено на CONNECTED.
  • При попытке открыть AudioTrack или AudioRecord нет звука (этот же код отлично работает, когда SCO не ошибается, т.е. проблема заключается в состоянии SCO, а не в вызовах AudioTrack/AudioRecord).
  • Вызов stopBluetoothSco() не приводит к состоянию сообщения о событии ОТКЛЮЧЕН.
  • setBluetoothScoOn (false/true) не имеет значения. Честно говоря, я не понимаю разницы в кажущемся избыточным "startBluetoothSco()/stopBluetoothSco()" и "setBluetoothScoOn (boolean)". Когда все работает, мой вызов startBluetoothSco() приводит к тому, что isBluetoothScoOn() возвращает true, заставляя меня думать, что мне не нужно вызывать setBluetoothScoOn (true).
  • Перезагрузка телефона не имеет значения.
  • Перезагрузка гарнитуры не имеет значения.
  • Переход на другую гарнитуру не имеет значения.
  • Иногда гарнитура теряет спаривание и должна быть повторно спарена.

Учитывая историю трека Google/Android в поддержке Bluetooth, это немного меня удивляет.

Может кто-то, пожалуйста, избавит меня от моего несчастья и определенно объяснит, как надежно начать и остановить Bluetooth SCO в Android?

PS: Есть ли официальный канал для эскалации таких проблем [w/Google? Samsung?] Или, есть ли StackOverflow мой лучший шанс найти фактический ответ?

Ответы

Ответ 1

В документации по Android многое отсутствует, однако, если вы звоните в startBluetoothSco() и stopBluetoothSco() каждый раз во время маршрутизации аудио, не должно быть проблем с правильной настройкой звука. Даже я видел, когда соединение долгое время простаивает, и мы запускаем StartBluetoothSco(), мы напрямую получаем разъединение. Чтобы решить эту проблему, я написал обходное решение, которое находится здесь: https://github.com/kodered/Bluetooth-Refresh-Logic

Надеюсь, что это поможет.