Android startBluetoothSco没有启动sco但isBluetoothScoOn返回true

swo*_*oby 18 android bluetooth headset

我创建了一个GitHub库与示例项目,显示了下面的问题我问这里:
https://github.com/paulpv/audio-loopback/tree/simplified/src/com/twistpair/wave/experimental/loopback
(请坚持使用"简化"分支并忽略"主"分支)

这两个主要文件是:

免责声明:我目前只使用一台运行CyanogenMod 10 Jelly Bean的三星Epic SPH-D700进行编码和测试.我没有在其他设备上试过这个,但也许这可能有助于阻止我拔掉头发并发疯.

我一直争取让Android的蓝牙SCO以可靠地启动和停止和捕获/回放音频几个月!
一旦我可以将手机转入SCO模式,通过AudioRecord和AudioTrack(分别)进行捕获和回放可以正常工作.
我遇到的问题是我无法可靠地将手机送入SCO模式!

使用startBluetoothSco()和setBluetoothScoOn(true)的"互联网"上的示例看起来都很简单直接,但是当我在我的设备上使用它们时,它们几乎永远无法可靠地工作.
我创建了自己的测试应用程序,除了启动和停止SCO之外什么都不做,我甚至无法让它可靠地工作!

我的代码监听BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED EXTRA_STATE==CONNECTED.
我可以可靠地检测何时连接或断开任何耳机.

当检测到连接时,我的处理程序立即调用startBluetoothSco().
A可能会发誓,至少有一次这已经踢到SCO_AUDIO_STATE了C ONNECTED,但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)
Run Code Online (Sandbox Code Playgroud)

我的应用程序开始关闭Jawbone耳机并更新UI ...

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

... UI更新完成
粘贴广播告诉我当前的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
Run Code Online (Sandbox Code Playgroud)

...当前SCO_AUDIO_STATE == DISCONNECTED; 预计,因为我的耳机已关闭.
我的SCO Disconnected事件监听器被调用并用两个sendMessages更新UI ...

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
Run Code Online (Sandbox Code Playgroud)

... UI更新完成

大约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)
Run Code Online (Sandbox Code Playgroud)

Jawbone连接; 我的事件监听器被称为......

10-03 17:00:37.626: I/MainActivity(29487): onBluetoothHeadsetConnected()
Run Code Online (Sandbox Code Playgroud)

......看到我们可以SCO ......

10-03 17:00:37.626: D/AudioStateManager(29487): mAudioManager.isBluetoothScoAvailableOffCall()=true
Run Code Online (Sandbox Code Playgroud)

...并自动调用startBluetoothSco()
这里是问题所在!为什么调用startBluetoothSco不会导致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();
Run Code Online (Sandbox Code Playgroud)

我的事件监听器完成w/sendMessage以更新当前BT状态的UI ...

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
Run Code Online (Sandbox Code Playgroud)

... UI更新完成
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)
Run Code Online (Sandbox Code Playgroud)

...从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
Run Code Online (Sandbox Code Playgroud)

...从CONNECTING转移到DISCONNECTED
我本来希望SCO从CONNECTING转到CONNECTED!
调用我的事件监听器并用两个sendMessages更新UI ...

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
Run Code Online (Sandbox Code Playgroud)

... UI更新完成

我等待~20秒连接SCO,但它永远不会到来.
我按下我的应用程序的"startBluetoothSco"按钮.
请注意,这导致完全相同的呼叫启动蓝牙科技()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();
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

...断开连接移动在连接
这是哪里的东西从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)
Run Code Online (Sandbox Code Playgroud)

...从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)
Run Code Online (Sandbox Code Playgroud)

...从CONNECTING移动到CONNECTED
事件用一个sendMessage更新UI

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
Run Code Online (Sandbox Code Playgroud)

... UI更新完成(说实话,我不确定第二次调用isBluetoothScoOn)

10-03 17:01:02.603: D/AudioStateManager(29487): mAudioManager.isBluetoothScoOn()=true
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

...从CONNECTING转移到CONNECTED

最后!
调用我的事件监听器并用两个sendMessages更新UI ...

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
Run Code Online (Sandbox Code Playgroud)

... UI更新完成

当我在一段时间后"手动"启动SCO时,一切(此时)都有效,但是如果我在连接耳机后立即自动启动SCO则不行.

更糟糕的是,当事情没有按预期发挥作用时,我会在SCO状态中看到奇怪的行为:

  • 延迟startBluetoothSco()一个合理的3-5秒似乎没有任何区别.我没有尝试过延迟超过5秒.等待超过5秒钟让音频开始流入你的BT耳机是荒谬的.
  • 有时调用isBluetoothScoOn()会返回true,即使我从未收到广播事件,因为上次DISCONNECTED状态表明状态已更改为CONNECTED.
  • 有时从UI"手动"调用startBluetoothSco()不会做任何事情,就好像SCO已经打开一样,但是自从上次DISCONNECTED状态说状态变为CONNECTED后我从未收到过任何广播事件.
  • 尝试在没有声音打开AudioTrack或AudioRecord结果(这相同的代码工作正常时,SCO没有行为不当的,即:问题是SCO的状态,而不是AudioTrack/AudioRecord调用).
  • 调用stopBluetoothSco()不会导致任何事件报告状态DISCONNECTED.
  • setBluetoothScoOn(false/true)没有区别.说实话,我不明白看似冗余的"startBluetoothSco()/ stopBluetoothSco()"和"setBluetoothScoOn(boolean)"的区别.当事情有效时,我的调用startBluetoothSco()会导致isBluetoothScoOn()返回true,这让我觉得我不需要调用setBluetoothScoOn(true).
  • 重新启动手机没有任何区别.
  • 重新启动耳机没有任何区别.
  • 换到另一个耳机也没什么区别.
  • 有时耳机失去配对,必须重新配对.

鉴于谷歌/ Android在蓝牙支持方面的记录,这一点让我感到惊讶.

有人可以让我摆脱我的痛苦,并明确解释如何在Android中可靠地启动和停止蓝牙SCO?

PS:是否有官方渠道来升级这样的问题[w/Google?三星?]?或者,StackOverflow是我找到实际答案的最佳机会吗?

Sau*_*rav 5

Android 文档中缺少很多内容,但是,如果您在音频路由期间每次调用 startBluetoothSco() 和 stopBluetoothSco(),那么正确路由音频应该没有问题。即使我已经看到当连接长时间保持空闲并且我们启动 StartBluetoothSco() 时,我们会直接断开连接。为了解决这个问题,我写了一个解决方法,在这里:https : //github.com/kodered/Bluetooth-Refresh-Logic

希望这可以帮助。