OSDN Git Service

Fix A2DP Suspend related multi-component deadlock
authorPavlin Radoslavov <pavlin@google.com>
Mon, 30 Apr 2018 20:39:20 +0000 (13:39 -0700)
committerPavlin Radoslavov <pavlin@google.com>
Tue, 1 May 2018 01:11:42 +0000 (18:11 -0700)
commited424de5bfff3ba318af64828f291e6e9ff74b3a
tree82b5242af3ea299b47ed3867b8df8d5ecb972d18
parent1058fe26148010f9313c9e0e8829fb6b856272b2
Fix A2DP Suspend related multi-component deadlock

Apparently, the processing of A2DP Suspend from the Audio HAL
could result in a deadlock across multiple components:
  A2dpService -> AudioService  -> Audio-BT HAL -> BT Native Stack ->
  BT JNI upcall -> A2dpService

1) A2dpService -> AudioService:
   - Waiting inside setActiveDevice() -> synchronized (mStateMachines) ->
   mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
   - The mAudioManager call can be blocking: a followup call would wait
     for the previous call to complete.
2) AudioService -> Audio-BT HAL
   The AudioService is wating on the Audio-BT HAL to complete its request.
   In this case the request is SUSPEND
3) Audio-BT HAL -> BT Native Stack
   The Audio side of the Audio-BT HAL is waiting for the BT Native stack
   to ACK the SUSPEND command.
   More specifically, it is retrying few times with timeout of 250ms
   each iteration.

4) BT Native Stack -> BT JNI upcall
   - In the BT Native Stack the BtifAv FSM (see file btif/src/btif_av.cc)
     is in state StateStarted and has received the
     BTIF_AV_SUSPEND_STREAM_REQ_EVT because of the SUSPEND command from the
     Audio HAL.
   - As a result, the LocalSuspendPending flag has been set:
     peer_.SetFlags(BtifAvPeer::kFlagLocalSuspendPending);
   - Only after the above flag is reset, then the BT Native Stack will
     ACK the SUSPEND command from the HAL
   - The above flag will be reset when event BTA_AV_SUSPEND_EVT is received
     from the underlying stack
   - The BtifAv state machine has received the BTA_AV_SUSPEND_EVT event.
     However, prior to clearing the pending status, the FSM tries to
     report the audio state via JNI to the Java layer:
       btif_report_audio_state();
       ...
       peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
  - Apparently, all the processing inside the BtifAv FSM is done on
    the JNI thread, including the btif_report_audio_state() upcall.
5) BT JNI upcall -> A2dpService
  - The btif_report_audio_state() upcall calls indirectly
    A2dpNativeInterface.onAudioStateChanged() -> sendMessageToService() ->
    A2dpService.messageFromNative()
  - Within the A2dpService.messageFromNative() processing, there is
    "synchronized (mStateMachines) {}" block, and the processing there
    blocks because per (1) above A2dpService.setActiveDevice() is
    already inside its own "synchronized (mStateMachines) {}" block

The above deadlock is fixed by the following:
 - Modified the BTA_AV_SUSPEND_EVT processing such that
   the clearing of the kFlagLocalSuspendPending flag is done BEFORE
   the btif_report_audio_state()
 - Modified the BtifAv FSM such that the internal processing of the FSM
   events is done on the BTA thread instead of the JNI thread. Thus,
   the FSM processing cannot be blocked by JNI upcalls.

Also:
 - Updated the do_in_bta_thread() function to have a return value
   so it is more aligned with the corresponding do_in_jni_thread()
   implementation
 - Updated the reordering of some other btif_report_*() upcalls inside
   btif_av.cc so they are after the FSM internal processing in the
   current state.
 - Added few extra logs to the Audio-BT HAL (file audio_a2dp_hw.cc).

Bug: 72823323
Test: Disconnect/reconnect with Philips SBH7000 Headset
Change-Id: I7ccb558e458a8e90d0414f94463bda8d4b3fdc97
Merged-In: I7ccb558e458a8e90d0414f94463bda8d4b3fdc97
(cherry picked from commit 2a3b9b597ee07f8728bd9903b0bc104c4e24fd6c)
audio_a2dp_hw/src/audio_a2dp_hw.cc
bta/include/bta_closure_api.h
bta/sys/bta_sys.h
bta/sys/bta_sys_main.cc
btif/co/bta_av_co.cc
btif/src/btif_av.cc