Failure to start playback on A2DP sink after connection
This is what happen:
after Headset is connected, we call start_audio_datapath which will send AVDTP_Start command to Headset,
Headset reject it with bad state. Bluedroid stack will ack failure to start_audio_datapath.
The next time we write audio data to bluetooth, we will call start_audio_datapath again to send AVDTP_Start command to Headset
Headset reject it with bad state again. Bluedroid stack will ack failure to start_audio_datapath.
When the third time we call start_audio_datapath, right at that time we receive AVDTP_Start command from Headset.
Handle AVDTP_Start command and Handle start_audio_datapath are in two different threads.
Handle AVDTP_Start command is in btu_task thread.
Handle start_audio_datapath() is in btif_task thread.
We have race condition in this case
Because when btif_task processed BTIF_AV_START_STREAM_REQ_EVT(triggered by start_audio_datapath),
it don't know we receive AVDTP_Start command which is processed in btu_task.
btif_task will send a message BTA_AV_API_START_EVT to btu_task, which will be handled by bta_av_do_start.
AVDTP_start command from headset is handled by bta_av_start_ok.
bta_av_start_ok will send BTA_AV_START_EVT with suspending true to btif_task and send AVDTP_Suspend command
to headset to suspend the AVDTP for reconfiguration purpose.
in bta_av_do_start, we will check whether the AVDTP is already started, we will know the AVDTP is already start at this time because
bta_av_do_start is also running in btu_task. We will send BTA_AV_START_EVT with success to btif_task.
In the btif_task, BTA_AV_START_EVT will be processed by btif_av_state_opened_handler:
For the first BTA_AV_START_EVT with suspending true sent by bta_av_start_ok, it will ignore it:
if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE))
return TRUE;
For the second BTA_AV_START_EVT with success sent by bta_av_do_start , it will ack success to start_audio_datapath, and change to
BTIF_AV_STATE_STARTED/BTAV_AUDIO_STATE_STARTED, after receive success ack from bluedroid stack, we will start send Audio data to bluetooth.
At last we received AVDTP_Suspend response accept from Headset, we will send BTA_AV_SUSPEND_EVT to btif_task, which will be
handled by btif_av_state_started_handler. It will call btif_a2dp_on_suspended and call audio_state_cb with new audio state
BTAV_AUDIO_STATE_STOPPED.
so The state between bluedroid stack and audio data path is out of sync.
The fix is to send failure message if we know we suspend AVDTP in bta_av_do_start,
also make sure we won't miss acknowledgement for pending start if we exit opened state,
to avoid audio data path dead lock.
bug:10953908
Change-Id: I1704839977324b7c4e234eb843cddf3719e10d2c
diff --git a/bta/av/bta_av_aact.c b/bta/av/bta_av_aact.c
index 9a38689..a50fb80 100644
--- a/bta/av/bta_av_aact.c
+++ b/bta/av/bta_av_aact.c
@@ -255,6 +255,28 @@
/*******************************************************************************
**
+** Function notify_start_failed
+**
+** Description notify up-layer AV start failed
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void notify_start_failed(tBTA_AV_SCB *p_scb)
+{
+ tBTA_AV_START start;
+ /* if start failed, clear role */
+ p_scb->role &= ~BTA_AV_ROLE_START_INT;
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_FAIL;
+ start.initiator = TRUE;
+ start.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+}
+
+/*******************************************************************************
+**
** Function bta_av_st_rc_timer
**
** Description start the AVRC timer if no RC connection & CT is supported &
@@ -1782,8 +1804,13 @@
else if (p_scb->started)
{
p_scb->role |= BTA_AV_ROLE_START_INT;
- if ( p_scb->wait == 0 )
- bta_av_start_ok(p_scb, NULL);
+ if ( p_scb->wait == 0 ) {
+ if (p_scb->role & BTA_AV_ROLE_SUSPEND) {
+ notify_start_failed(p_scb);
+ } else {
+ bta_av_start_ok(p_scb, NULL);
+ }
+ }
}
APPL_TRACE_DEBUG2("started %d role:x%x", p_scb->started, p_scb->role);
}
@@ -2233,19 +2260,10 @@
*******************************************************************************/
void bta_av_start_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
- tBTA_AV_START start;
-
if(p_scb->started == FALSE && p_scb->co_started == FALSE)
{
- /* if start failed, clear role */
- p_scb->role &= ~BTA_AV_ROLE_START_INT;
-
bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
- start.chnl = p_scb->chnl;
- start.status = BTA_AV_FAIL;
- start.initiator = TRUE;
- start.hndl = p_scb->hndl;
- (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+ notify_start_failed(p_scb);
}
bta_sys_set_policy(BTA_ID_AV, (HCI_ENABLE_SNIFF_MODE|HCI_ENABLE_MASTER_SLAVE_SWITCH), p_scb->peer_addr);
diff --git a/btif/include/btif_media.h b/btif/include/btif_media.h
index 9e20e21..4cdbb8c 100755
--- a/btif/include/btif_media.h
+++ b/btif/include/btif_media.h
@@ -236,7 +236,7 @@
void btif_a2dp_setup_codec(void);
void btif_a2dp_on_idle(void);
void btif_a2dp_on_open(void);
-void btif_a2dp_on_started(tBTA_AV_START *p_av);
+BOOLEAN btif_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start);
void btif_a2dp_ack_fail(void);
void btif_a2dp_on_stop_req(void);
void btif_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av);
diff --git a/btif/src/btif_av.c b/btif/src/btif_av.c
index 748b204..f173f6a 100755
--- a/btif/src/btif_av.c
+++ b/btif/src/btif_av.c
@@ -491,14 +491,21 @@
if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE))
return TRUE;
- btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
- btif_a2dp_on_started(&p_av->start);
+ if (btif_a2dp_on_started(&p_av->start,
+ ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) != 0))) {
+ /* only clear pending flag after acknowledgement */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ }
/* remain in open state if status failed */
if (p_av->start.status != BTA_AV_SUCCESS)
return FALSE;
- /* change state to started */
+ /* change state to started, send acknowledgement if start is pending */
+ if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ btif_a2dp_on_started(NULL, TRUE);
+ /* pending start flag will be cleared when exit current state */
+ }
btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_STARTED);
} break;
@@ -520,6 +527,11 @@
HAL_CBACK(bt_av_callbacks, connection_state_cb,
BTAV_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+ /* change state to idle, send acknowledgement if start is pending */
+ if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ btif_a2dp_ack_fail();
+ /* pending start flag will be cleared when exit current state */
+ }
btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
break;
@@ -581,7 +593,7 @@
case BTIF_AV_START_STREAM_REQ_EVT:
/* we were remotely started, just ack back the local request */
- btif_a2dp_on_started(NULL);
+ btif_a2dp_on_started(NULL, TRUE);
break;
/* fixme -- use suspend = true always to work around issue with BTA AV */
diff --git a/btif/src/btif_media_task.c b/btif/src/btif_media_task.c
index 7fe04fd..ff9f92c 100755
--- a/btif/src/btif_media_task.c
+++ b/btif/src/btif_media_task.c
@@ -844,9 +844,10 @@
**
*******************************************************************************/
-void btif_a2dp_on_started(tBTA_AV_START *p_av)
+BOOLEAN btif_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start)
{
tBTIF_STATUS status;
+ BOOLEAN ack = FALSE;
APPL_TRACE_EVENT0("## ON A2DP STARTED ##");
@@ -854,7 +855,7 @@
{
/* ack back a local start request */
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
- return;
+ return TRUE;
}
if (p_av->status == BTA_AV_SUCCESS)
@@ -863,7 +864,10 @@
{
if (p_av->initiator)
{
- a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
+ if (pending_start) {
+ a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
+ ack = TRUE;
+ }
}
else
{
@@ -875,10 +879,12 @@
/* media task is autostarted upon a2dp audiopath connection */
}
}
- else
+ else if (pending_start)
{
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_FAILURE);
+ ack = TRUE;
}
+ return ack;
}