25

ここで質問している以下の問題を示すサンプルプロジェクトを使用してGitHubリポジトリを作成しました:
https
://github.com/paulpv/audio-loopback/tree/simplified/src/com/twistpair/wave/experimental/loopback (「簡略化された」ブランチに固執し、「マスター」ブランチを無視してください)

2つの主要なファイルは次のとおりです。

免責事項:私は現在、CyanogenMod 10JellyBeanを実行しているSamsungEpicSPH-D700を1つだけ使用して、これをコーディングおよびテストしています。私は他のデバイスでこれを試したことがありませんが、おそらくそれは私が私の髪を引っ張って狂うのを防ぐのに役立つかもしれません。

私はAndroidBluetoothSCOを確実に開始および停止し、オーディオを数か月間キャプチャ/再生するために戦ってきました!
電話をSCOモードにできるようになると、AudioRecordとAudioTrackを介したキャプチャと再生は(それぞれ)文書化されているように正常に機能します。
私が抱えている問題は、電話を確実にSCOモードにすることができないことです!

startBluetoothSco()とsetBluetoothScoOn(true)を使用する「インターネット」の例はすべて単純でわかりやすいように見えますが、デバイスで使用すると、確実に機能することはほとんどありません。
SCOの開始と停止だけを行う独自のテストアプリを作成しましたが、これを確実に機能させることすらできません。

BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED EXTRA_STATE==CONNECTED.
私のコードは、ヘッドセットが接続または切断されたことを確実に検出できることをリッスンします。

接続を検出すると、ハンドラーはすぐにstartBluetoothSco()を呼び出します。
Aは、少なくとも1回はSCO_AUDIO_STATEC 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)

Jawboneヘッドセットをオフにするとアプリが起動し、UIが更新されます...

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

...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

...現在のSCO_AUDIO_STATE==DISCONNECTED; 私のヘッドセットがオフになっているので、予想されます。
SCO切断イベントリスナーが呼び出され、2つの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

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

ジョーボーン接続; 私のイベントリスナーは...

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

...私たちがSCOできることを確認します...

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

...そして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();

私のイベントリスナーは、現在のBT状態でUIを更新するためのsendMessageで終了します。

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

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

...DISCONNECTEDからCONNECTINGに移動しまし
たstartBluetoothScoからの2番目の結果が入ります...

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からDISCONNECTED
に移動しましたSCOがCONNECTINGからCONNECTEDに移行することを期待していました!
私のイベントリスナーが呼び出され、2つの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

...UIの更新が完了しました

SCOが接続されるまで約20秒待ちますが、接続されません。
アプリの「startBluetoothSco」ボタンを押します。
これは、17:00:37.626にBluetoothSco()を開始するためのまったく同じ呼び出しであることに注意してください。

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に移動
ここでは、17:00:37.626でのstartBluetoothSco()の自動呼び出しとは異なります。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からCONNECTED
イベントに移動し、1つの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

... UIの更新が完了しました(正直なところ、2回目に何がBluetoothScoOnを呼び出しているのかわかりません)

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

startBluetoothScoからの2番目の結果が入ります...

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に移動しました

ついに!
私のイベントリスナーが呼び出され、2つの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

...UIの更新が完了しました

しばらくしてから「手動で」SCOを起動すると(今回は)すべてが機能しますが、ヘッドセットを接続した直後に自動的にSCOを起動した場合は機能しません。

さらに悪いことに、物事が期待どおりに機能していない場合、SCOの状態で奇妙な動作が見られます。

  • startBluetoothSco()を妥当な3〜5秒間遅らせても、違いはないようです。私はそれを5秒以上遅らせようとしませんでした。オーディオがBTヘッドセットに流れ始めるのを5秒以上待つのは、ばかげています。
  • 最後のDISCONNECTED状態以降、状態がCONNECTEDに変更されたことを示すブロードキャストイベントを受信したことがない場合でも、isBluetoothScoOn()を呼び出すとtrueが返されることがあります。
  • 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)を呼び出す必要がないと思います。
  • 電話を再起動しても違いはありません。
  • ヘッドセットを再起動しても違いはありません。
  • 別のヘッドセットに変更しても違いはありません。
  • ヘッドセットがペアリングを失い、再ペアリングが必要になる場合があります。

Bluetoothサポートに関するGoogle/Androidの実績を考えると、これにはほとんど驚かされません。

誰かが私の悲惨さから私を解放し、AndroidでBluetooth SCOを確実に開始および停止する方法を明確に説明できますか?

PS:このような問題をエスカレートする公式チャンネルはありますか[w / Google?サムスン?]?または、StackOverflowは実際の答えを見つけるための私の最高のチャンスですか?

4

1 に答える 1

5

Androidのドキュメントには多くの欠落がありますが、オーディオルーティング中に毎回startBluetoothSco()とstopBluetoothSco()を呼び出すと、オーディオを正しくルーティングするのに問題はありません。接続が長時間アイドル状態になり、StartBluetoothSco()を開始すると、直接切断されるのを見てきました。これを解決するために、私はここにある回避策を書きました: https ://github.com/kodered/Bluetooth-Refresh-Logic

お役に立てれば。

于 2015-03-06T07:53:18.630 に答える