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)