/******************************************************************************
* Copyright (C) 2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
******************************************************************************/
/******************************************************************************
*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2009-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#define LOG_TAG "bt_btif_a2dp_source"
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include "bt_target.h"
#include
#ifndef OS_GENERIC
#include
#endif
#include
#include
#include
#if (OFF_TARGET_TEST_ENABLED == FALSE)
#include "audio_hal_interface/a2dp_encoding.h"
#include "audio_a2dp_hw/include/audio_a2dp_hw.h"
#endif
#if (OFF_TARGET_TEST_ENABLED == TRUE)
#include "a2dp_hal_sim/audio_a2dp_hal.h"
#include "a2dp_hal_sim/audio_a2dp_hal_stub.h"
using ::bluetooth::audio::a2dp::SessionType;
#endif
#include "bt_common.h"
#include "bta_av_ci.h"
#include "btif_a2dp.h"
#include "btif_a2dp_control.h"
#include "btif_a2dp_source.h"
#include "btif_av.h"
#include "btif_av_co.h"
#include "btif_util.h"
#include "osi/include/fixed_queue.h"
#include "osi/include/log.h"
#include "osi/include/metrics.h"
#include "osi/include/mutex.h"
#include "osi/include/osi.h"
#include "osi/include/thread.h"
#include "osi/include/time.h"
#include "uipc.h"
#include "btif_a2dp_audio_interface.h"
#include "btif_bat.h"
#include "btif_hf.h"
#include "btif_av.h"
#include "bta_sys.h"
using system_bt_osi::BluetoothMetricsLogger;
using system_bt_osi::A2dpSessionMetrics;
/**
* The typical runlevel of the tx queue size is ~1 buffer
* but due to link flow control or thread preemption in lower
* layers we might need to temporarily buffer up data.
*/
#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (MAX_PCM_FRAME_NUM_PER_TICK * 2)
#define BTIF_UNBLOCK_AUDIO_START_TOUT 3000
#define BTIF_REMOTE_START_TOUT 3000
enum {
BTIF_A2DP_SOURCE_STATE_OFF,
BTIF_A2DP_SOURCE_STATE_STARTING_UP,
BTIF_A2DP_SOURCE_STATE_RUNNING,
BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN
};
/* BTIF Media Source event definition */
enum {
BTIF_MEDIA_AUDIO_TX_START = 1,
BTIF_MEDIA_AUDIO_TX_STOP,
BTIF_MEDIA_AUDIO_TX_FLUSH,
BTIF_MEDIA_SOURCE_ENCODER_INIT,
BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE,
BTIF_MEDIA_AUDIO_FEEDING_UPDATE,
BTIF_MEDIA_RESET_VS_STATE
};
#define MAX_MEDIA_WORKQUEUE_SEM_COUNT 4096
/* tBTIF_A2DP_SOURCE_ENCODER_INIT msg structure */
typedef struct {
BT_HDR hdr;
tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
} tBTIF_A2DP_SOURCE_ENCODER_INIT;
/* tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE msg structure */
typedef struct {
BT_HDR hdr;
RawAddress bd_addr;
btav_a2dp_codec_config_t user_config;
} tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE;
/* tBTIF_A2DP_AUDIO_FEEDING_UPDATE msg structure */
typedef struct {
BT_HDR hdr;
btav_a2dp_codec_config_t feeding_params;
} tBTIF_A2DP_AUDIO_FEEDING_UPDATE;
tBTIF_A2DP_SOURCE_CB btif_a2dp_source_cb;
tBTIF_A2DP_SOURCE_VSC btif_a2dp_src_vsc;
static int btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
extern bool enc_update_in_progress;
extern bool reconfig_a2dp;
extern bool tx_enc_update_initiated;
extern bool is_block_hal_start;
static void btif_a2dp_source_command_ready(fixed_queue_t* queue, void* context);
static void btif_a2dp_source_startup_delayed(void* context);
static void btif_a2dp_source_shutdown_delayed(void* context);
static uint8_t btif_a2dp_source_dynamic_audio_buffer_size =
MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ;
static void btif_a2dp_source_audio_tx_start_event(void);
static void btif_a2dp_source_audio_tx_stop_event(void);
static void btif_a2dp_source_audio_tx_flush_event(BT_HDR* p_msg);
static void btif_a2dp_source_encoder_init_event(BT_HDR* p_msg);
static void btif_a2dp_source_encoder_user_config_update_event(BT_HDR* p_msg);
static void btif_a2dp_source_audio_feeding_update_event(BT_HDR* p_msg);
void btif_a2dp_source_encoder_init(void);
static void btif_a2dp_source_encoder_init_req(
tBTIF_A2DP_SOURCE_ENCODER_INIT* p_msg);
static bool btif_a2dp_source_audio_tx_flush_req(void);
static void btif_a2dp_source_alarm_cb(void* context);
static void btif_a2dp_source_audio_handle_timer(void* context);
static uint32_t btif_a2dp_source_read_callback(uint8_t* p_buf, uint32_t len);
static bool btif_a2dp_source_enqueue_callback(BT_HDR* p_buf, size_t frames_n,
uint32_t bytes_read);
static void log_tstamps_us(const char* comment, uint64_t timestamp_us);
static void update_scheduling_stats(scheduling_stats_t* stats, uint64_t now_us,
uint64_t expected_delta);
static void btm_read_rssi_cb(void* data);
static void btm_read_failed_contact_counter_cb(void* data);
static void btm_read_automatic_flush_timeout_cb(void* data);
static void btm_read_tx_power_cb(void* data);
static void btif_a2dp_source_unblock_audio_start_timeout(void* context);
static void btif_a2dp_source_remote_start_timeout(void* context);
extern void btif_av_set_offload_status(void);
extern int btif_max_av_clients;
extern int btif_get_is_remote_started_idx();
extern bool audio_start_awaited;
extern void btif_av_reset_reconfig_flag();
extern bool btif_av_is_remote_started_set(int index);
extern tBTA_AV_HNDL btif_av_get_av_hdl_from_idx(int idx);
extern bool enc_update_in_progress;
extern bool btif_av_is_state_opened(int i);
extern tBTIF_A2DP_SOURCE_VSC btif_a2dp_src_vsc;
extern bool btif_av_is_local_started_on_other_idx(int current_index);
extern bool btif_av_current_device_is_tws();
extern bool btif_av_is_tws_device_playing(int index);
extern bool btif_av_is_idx_tws_device(int index);
extern int btif_av_get_tws_pair_idx(int index);
extern void btif_av_clear_pending_start_flag();
extern bool btif_av_is_tws_suspend_triggered(int index);
static char a2dp_hal_imp[PROPERTY_VALUE_MAX] = "false";
UNUSED_ATTR static const char* dump_media_event(uint16_t event) {
switch (event) {
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_START)
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_STOP)
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_TX_FLUSH)
CASE_RETURN_STR(BTIF_MEDIA_SOURCE_ENCODER_INIT)
CASE_RETURN_STR(BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE)
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_UPDATE)
CASE_RETURN_STR(BTIF_MEDIA_RESET_VS_STATE)
default:
break;
}
return "UNKNOWN A2DP SOURCE EVENT";
}
void btif_a2dp_source_accumulate_scheduling_stats(scheduling_stats_t* src,
scheduling_stats_t* dst) {
dst->total_updates += src->total_updates;
dst->last_update_us = src->last_update_us;
dst->overdue_scheduling_count += src->overdue_scheduling_count;
dst->total_overdue_scheduling_delta_us +=
src->total_overdue_scheduling_delta_us;
dst->max_overdue_scheduling_delta_us =
std::max(dst->max_overdue_scheduling_delta_us,
src->max_overdue_scheduling_delta_us);
dst->premature_scheduling_count += src->premature_scheduling_count;
dst->total_premature_scheduling_delta_us +=
src->total_premature_scheduling_delta_us;
dst->max_premature_scheduling_delta_us =
std::max(dst->max_premature_scheduling_delta_us,
src->max_premature_scheduling_delta_us);
dst->exact_scheduling_count += src->exact_scheduling_count;
dst->total_scheduling_time_us += src->total_scheduling_time_us;
}
void btif_a2dp_source_accumulate_stats(btif_media_stats_t* src,
btif_media_stats_t* dst) {
dst->tx_queue_total_frames += src->tx_queue_total_frames;
dst->tx_queue_max_frames_per_packet = std::max(
dst->tx_queue_max_frames_per_packet, src->tx_queue_max_frames_per_packet);
dst->tx_queue_total_queueing_time_us += src->tx_queue_total_queueing_time_us;
dst->tx_queue_max_queueing_time_us = std::max(
dst->tx_queue_max_queueing_time_us, src->tx_queue_max_queueing_time_us);
dst->tx_queue_total_readbuf_calls += src->tx_queue_total_readbuf_calls;
dst->tx_queue_last_readbuf_us = src->tx_queue_last_readbuf_us;
dst->tx_queue_total_flushed_messages += src->tx_queue_total_flushed_messages;
dst->tx_queue_last_flushed_us = src->tx_queue_last_flushed_us;
dst->tx_queue_total_dropped_messages += src->tx_queue_total_dropped_messages;
dst->tx_queue_max_dropped_messages = std::max(
dst->tx_queue_max_dropped_messages, src->tx_queue_max_dropped_messages);
dst->tx_queue_dropouts += src->tx_queue_dropouts;
dst->tx_queue_last_dropouts_us = src->tx_queue_last_dropouts_us;
dst->media_read_total_underflow_bytes +=
src->media_read_total_underflow_bytes;
dst->media_read_total_underflow_count +=
src->media_read_total_underflow_count;
dst->media_read_last_underflow_us = src->media_read_last_underflow_us;
btif_a2dp_source_accumulate_scheduling_stats(&src->tx_queue_enqueue_stats,
&dst->tx_queue_enqueue_stats);
btif_a2dp_source_accumulate_scheduling_stats(&src->tx_queue_dequeue_stats,
&dst->tx_queue_dequeue_stats);
memset(src, 0, sizeof(btif_media_stats_t));
}
bool btif_a2dp_source_startup(void) {
if (btif_a2dp_source_state != BTIF_A2DP_SOURCE_STATE_OFF) {
APPL_TRACE_ERROR("%s: A2DP Source media task already running", __func__);
return false;
}
memset(&btif_a2dp_source_cb, 0, sizeof(btif_a2dp_source_cb));
btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_STARTING_UP;
btif_a2dp_source_cb.last_remote_started_index = -1;
btif_a2dp_source_cb.last_started_index_pointer = NULL;
APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##");
/* Start A2DP Source media task */
btif_a2dp_source_cb.worker_thread =
thread_new_sized("media_worker", MAX_MEDIA_WORKQUEUE_SEM_COUNT);
if (btif_a2dp_source_cb.worker_thread == NULL) {
APPL_TRACE_ERROR("%s: unable to start up media thread", __func__);
btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
return false;
}
btif_a2dp_source_cb.tx_audio_queue = fixed_queue_new(SIZE_MAX);
btif_a2dp_source_cb.cmd_msg_queue = fixed_queue_new(SIZE_MAX);
fixed_queue_register_dequeue(
btif_a2dp_source_cb.cmd_msg_queue,
thread_get_reactor(btif_a2dp_source_cb.worker_thread),
btif_a2dp_source_command_ready, NULL);
APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##");
/* Schedule the rest of the startup operations */
thread_post(btif_a2dp_source_cb.worker_thread,
btif_a2dp_source_startup_delayed, NULL);
return true;
}
static void btif_a2dp_source_startup_delayed(UNUSED_ATTR void* context) {
#if (OFF_TARGET_TEST_ENABLED == FALSE)
raise_priority_a2dp(TASK_HIGH_MEDIA);
#endif
if (!btif_a2dp_source_is_hal_v2_supported()) {
btif_a2dp_control_init();
}
btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_RUNNING;
APPL_TRACE_EVENT("%s: enc_update_in_progress = %d", __func__, enc_update_in_progress);
enc_update_in_progress = FALSE;
BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
}
void btif_a2dp_source_shutdown(void) {
if ((btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) ||
(btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN)) {
return;
}
/* Make sure no channels are restarted while shutting down */
btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN;
APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##");
if (btif_a2dp_source_cb.unblock_audio_start_alarm != NULL) {
alarm_free(btif_a2dp_source_cb.unblock_audio_start_alarm);
btif_a2dp_source_cb.unblock_audio_start_alarm = NULL;
}
// Stop the timer
alarm_free(btif_a2dp_source_cb.media_alarm);
btif_a2dp_source_cb.media_alarm = NULL;
btif_a2dp_source_cancel_remote_start();
btif_dispatch_sm_event(BTIF_AV_RESET_REMOTE_STARTED_FLAG_EVT, NULL, 0);
// Exit the thread
fixed_queue_free(btif_a2dp_source_cb.cmd_msg_queue, NULL);
btif_a2dp_source_cb.cmd_msg_queue = NULL;
thread_post(btif_a2dp_source_cb.worker_thread,
btif_a2dp_source_shutdown_delayed, NULL);
thread_free(btif_a2dp_source_cb.worker_thread);
btif_a2dp_source_cb.worker_thread = NULL;
APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STOPPED ##");
}
static void btif_a2dp_source_shutdown_delayed(UNUSED_ATTR void* context) {
APPL_TRACE_DEBUG("%s", __func__);
btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
if (btif_a2dp_source_is_hal_v2_supported()){
#if AHIM_ENABLED
btif_ahim_cleanup_hal(A2DP);
#else
bluetooth::audio::a2dp::cleanup();
#endif
} else {
btif_a2dp_control_cleanup();
}
fixed_queue_free(btif_a2dp_source_cb.tx_audio_queue, NULL);
btif_a2dp_source_cb.tx_audio_queue = NULL;
btif_a2dp_source_state = BTIF_A2DP_SOURCE_STATE_OFF;
APPL_TRACE_EVENT("%s: enc_update_in_progress = %d", __func__, enc_update_in_progress);
enc_update_in_progress = FALSE;
}
bool btif_a2dp_source_media_task_is_running(void) {
return (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_RUNNING);
}
bool btif_a2dp_source_media_task_is_shutting_down(void) {
return (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_SHUTTING_DOWN);
}
bool btif_a2dp_source_is_streaming(void) {
return alarm_is_scheduled(btif_a2dp_source_cb.media_alarm);
}
bool btif_a2dp_source_is_remote_start(void) {
return alarm_is_scheduled(btif_a2dp_source_cb.remote_start_alarm);
}
int btif_a2dp_source_last_remote_start_index() {
APPL_TRACE_DEBUG("%s:remote started set for device index %d",
__func__, btif_a2dp_source_cb.last_remote_started_index);
return btif_a2dp_source_cb.last_remote_started_index;
}
void btif_a2dp_source_cancel_remote_start() {
APPL_TRACE_DEBUG("%s", __func__);
if (btif_a2dp_source_cb.remote_start_alarm != NULL) {
btif_av_clear_remote_start_timer(btif_a2dp_source_cb.last_remote_started_index);
btif_a2dp_source_cb.remote_start_alarm = NULL;
APPL_TRACE_DEBUG("%s: cancel remote start on AV index %d",
__func__, btif_a2dp_source_cb.last_remote_started_index);
btif_a2dp_source_cb.last_remote_started_index = -1;
if(btif_a2dp_source_cb.last_started_index_pointer)
osi_free(btif_a2dp_source_cb.last_started_index_pointer);
btif_a2dp_source_cb.last_started_index_pointer = NULL;
}
return;
}
static void btif_media_remote_start_alarm_cb(UNUSED_ATTR void *context) {
thread_post(btif_a2dp_source_cb.worker_thread,
btif_a2dp_source_remote_start_timeout, context);
}
static void btif_a2dp_source_remote_start_timeout(UNUSED_ATTR void* context) {
int *arg = (int *)context;
if (!arg)
return;
int index = *arg;
APPL_TRACE_DEBUG("%s: Suspend stream request to Av index %d", __func__, *arg);
if (btif_a2dp_source_cb.remote_start_alarm != NULL &&
index == btif_a2dp_source_cb.last_remote_started_index) {
alarm_free(btif_a2dp_source_cb.remote_start_alarm);
btif_a2dp_source_cb.remote_start_alarm = NULL;
btif_a2dp_source_cb.last_remote_started_index = -1;
btif_a2dp_source_cb.last_started_index_pointer = NULL;
if(arg) {
osi_free(arg);
}
}
btif_dispatch_sm_event(BTIF_AV_REMOTE_SUSPEND_STREAM_REQ_EVT, &index, sizeof(index));
return;
}
void btif_a2dp_source_on_remote_start(struct alarm_t **remote_start_alarm, int index) {
// initiate remote start timer for index basis
int *arg = NULL;
if (remote_start_alarm == NULL) {
LOG_ERROR(LOG_TAG,"%s:remote start alarm is NULL",__func__);
return;
}
arg = (int *) osi_malloc(sizeof(int));
if (!arg) {
LOG_ERROR(LOG_TAG,"%s: alloc fail.", __func__);
return;
}
*remote_start_alarm = alarm_new("btif.remote_start_task");
if (!(*remote_start_alarm)) {
LOG_ERROR(LOG_TAG,"%s:unable to allocate media alarm",__func__);
osi_free(arg);
btif_av_clear_remote_start_timer(index);
btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, &index, sizeof(index));
return;
}
*arg = index;
alarm_set(*remote_start_alarm, BTIF_REMOTE_START_TOUT,
btif_media_remote_start_alarm_cb, (void *)arg);
btif_a2dp_source_cb.remote_start_alarm = *remote_start_alarm;
btif_a2dp_source_cb.last_remote_started_index = index;
btif_a2dp_source_cb.last_started_index_pointer = arg;
APPL_TRACE_DEBUG("%s: Remote start timer started index %d arg %d",
__func__, index, *arg);
}
static void btif_media_unblock_audio_start_alarm_cb(UNUSED_ATTR void *context) {
thread_post(btif_a2dp_source_cb.worker_thread,
btif_a2dp_source_unblock_audio_start_timeout, NULL);
}
void btif_trigger_unblock_audio_start_recovery_timer() {
// Clear any existing timer before allocate
btif_a2dp_source_cancel_unblock_audio_start();
// Allocate new timer
btif_a2dp_source_cb.unblock_audio_start_alarm = alarm_new("btif.unblock_audio_start_task");
if (!btif_a2dp_source_cb.unblock_audio_start_alarm) {
LOG_ERROR(LOG_TAG,"%s:unable to allocate unblock start alarm",__func__);
return;
}
alarm_set(btif_a2dp_source_cb.unblock_audio_start_alarm, BTIF_UNBLOCK_AUDIO_START_TOUT,
btif_media_unblock_audio_start_alarm_cb, NULL);
APPL_TRACE_DEBUG("%s: Unblock Audio start timer started", __func__);
}
void btif_a2dp_source_cancel_unblock_audio_start(void) {
APPL_TRACE_DEBUG("%s: Unblock Audio start timer cancelled", __func__);
if (btif_a2dp_source_cb.unblock_audio_start_alarm != NULL) {
alarm_free(btif_a2dp_source_cb.unblock_audio_start_alarm);
btif_a2dp_source_cb.unblock_audio_start_alarm = NULL;
}
return;
}
static void btif_a2dp_source_unblock_audio_start_timeout(void* context) {
APPL_TRACE_DEBUG("%s: After 2 sec timeout unblock the Audio Start timer", __func__);
if (is_block_hal_start) {
is_block_hal_start = false;
}
}
static void btif_a2dp_source_command_ready(fixed_queue_t* queue,
UNUSED_ATTR void* context) {
BT_HDR* p_msg = (BT_HDR*)fixed_queue_dequeue(queue);
LOG_DEBUG(LOG_TAG, "%s: event: %d %s", __func__, p_msg->event,
dump_media_event(p_msg->event));
switch (p_msg->event) {
case BTIF_MEDIA_AUDIO_TX_START:
btif_a2dp_source_audio_tx_start_event();
break;
case BTIF_MEDIA_AUDIO_TX_STOP:
btif_a2dp_source_audio_tx_stop_event();
break;
case BTIF_MEDIA_AUDIO_TX_FLUSH:
btif_a2dp_source_audio_tx_flush_event(p_msg);
break;
case BTIF_MEDIA_SOURCE_ENCODER_INIT:
btif_a2dp_source_encoder_init_event(p_msg);
break;
case BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE:
btif_a2dp_source_encoder_user_config_update_event(p_msg);
break;
case BTIF_MEDIA_AUDIO_FEEDING_UPDATE:
btif_a2dp_source_audio_feeding_update_event(p_msg);
break;
case BTIF_MEDIA_RESET_VS_STATE:
btif_a2dp_src_vsc.tx_started = FALSE;
btif_a2dp_src_vsc.tx_stop_initiated = FALSE;
btif_a2dp_src_vsc.vs_configs_exchanged = FALSE;
btif_a2dp_src_vsc.tx_start_initiated = FALSE;
tx_enc_update_initiated = FALSE;
break;
default:
APPL_TRACE_ERROR("ERROR in %s unknown event %d", __func__, p_msg->event);
break;
}
LOG_VERBOSE(LOG_TAG, "%s: %s DONE", __func__, dump_media_event(p_msg->event));
osi_free(p_msg);
}
bt_status_t btif_a2dp_source_setup_codec(tBTA_AV_HNDL hndl) {
APPL_TRACE_EVENT("## A2DP SOURCE SETUP CODEC ##");
bt_status_t status = BT_STATUS_FAIL;
mutex_global_lock();
status = bta_av_set_a2dp_current_codec(hndl);
if (status == BT_STATUS_SUCCESS) {
/* Init the encoding task */
btif_a2dp_source_encoder_init();
A2dpCodecConfig* current_codec = bta_av_get_a2dp_current_codec();
btav_a2dp_codec_config_t codec_config;
//get the current codec config, so that we can get the codec type.
if (current_codec != nullptr) {
codec_config = current_codec->getCodecConfig();
} else {
APPL_TRACE_ERROR("%s: current codec is null, returns fail.", __func__);
mutex_global_unlock();
return BT_STATUS_FAIL;
}
APPL_TRACE_DEBUG("%s: codec_config.codec_type:%d", __func__, codec_config.codec_type);
uint8_t p_codec_info[AVDT_CODEC_SIZE];
memset(p_codec_info, 0, AVDT_CODEC_SIZE);
//copy peer codec info to p_codec_info
if (!current_codec->copyOutOtaCodecConfig(p_codec_info)) {
APPL_TRACE_ERROR("%s: Fetching peer codec info returns fail.", __func__);
mutex_global_unlock();
return BT_STATUS_FAIL;
}
//int index = 0;
//index = HANDLE_TO_INDEX(hndl);
RawAddress peer_bda;
btif_av_get_active_peer_addr(&peer_bda);
tBT_FLOW_SPEC flow_spec;
memset(&flow_spec, 0x00, sizeof(flow_spec));
flow_spec.flow_direction = 0x00; /* flow direction - out going */
flow_spec.service_type = 0x02; /* Guaranteed */
flow_spec.token_rate = 0x00; /* bytes/second - no token rate is specified*/
flow_spec.token_bucket_size = 0x00; /* bytes - no token bucket is needed*/
flow_spec.latency = 0xFFFFFFFF; /* microseconds - default value */
if (codec_config.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_SBC) {
flow_spec.peak_bandwidth = (345*1000)/8; /* bytes/second */
} else if (codec_config.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
flow_spec.peak_bandwidth = (380*1000)/8; /* bytes/second */
} else if (codec_config.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD) {
flow_spec.peak_bandwidth = (660*1000)/8; /* bytes/second */
} else if (codec_config.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC) {
/* For ABR mode default peak bandwidth is 0, for static it will be fetched */
uint32_t bitrate = 0;
bitrate = A2DP_GetTrackBitRate(p_codec_info);
APPL_TRACE_DEBUG(LOG_TAG,"bitrate = %d", bitrate);
flow_spec.peak_bandwidth = bitrate/8; /* bytes/second */
} else if (codec_config.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_AAC) {
if (btif_av_is_split_a2dp_enabled()) {
char prop_value[PROPERTY_VALUE_MAX] = "false";
property_get("persist.vendor.qcom.bluetooth.aac_abr_support", prop_value, "false");
ALOGE("%s: AAC ABR support : %s", __func__, prop_value);
if (!strcmp(prop_value, "true")) {
flow_spec.peak_bandwidth = 0;//ABR enabled
} else {
flow_spec.peak_bandwidth = (165*1000)/8; /* bytes/second */
}
} else {
flow_spec.peak_bandwidth = (320*1000)/8; /* bytes/second */
}
}
APPL_TRACE_DEBUG("%s: peak_bandwidth: %d", __func__, flow_spec.peak_bandwidth);
BTM_FlowSpec (peer_bda, &flow_spec, NULL);
} else {
APPL_TRACE_ERROR("%s() can not setup current codec", __func__);
status = BT_STATUS_FAIL;
}
if (btif_a2dp_source_is_hal_v2_supported()) {
APPL_TRACE_EVENT("%s ## setup_codec ##", __func__);
#if AHIM_ENABLED
btif_ahim_setup_codec(A2DP);
#else
bluetooth::audio::a2dp::setup_codec();
#endif
}
mutex_global_unlock();
return status;
}
void btif_a2dp_source_start_audio_req(void) {
BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
BTIF_TRACE_DEBUG("%s:", __func__);
p_buf->event = BTIF_MEDIA_AUDIO_TX_START;
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
memset(&btif_a2dp_source_cb.stats, 0, sizeof(btif_media_stats_t));
// Assign session_start_us to 1 when time_get_os_boottime_us() is 0 to
// indicate btif_a2dp_source_start_audio_req() has been called
btif_a2dp_source_cb.stats.session_start_us = time_get_os_boottime_us();
if (btif_a2dp_source_cb.stats.session_start_us == 0) {
btif_a2dp_source_cb.stats.session_start_us = 1;
}
btif_a2dp_source_cb.stats.session_end_us = 0;
}
void btif_a2dp_source_stop_audio_req(void) {
BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
tA2DP_CTRL_CMD pending_cmd = A2DP_CTRL_CMD_NONE;
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
pending_cmd = btif_ahim_get_pending_command(A2DP);
#else
pending_cmd = bluetooth::audio::a2dp::get_pending_command();
#endif
} else {
pending_cmd = btif_a2dp_control_get_pending_command();
}
p_buf->event = BTIF_MEDIA_AUDIO_TX_STOP;
/*
* Explicitly check whether btif_a2dp_source_cb.cmd_msg_queue is not NULL
* to avoid a race condition during shutdown of the Bluetooth stack.
* This race condition is triggered when A2DP audio is streaming on
* shutdown:
* "btif_a2dp_source_on_stopped() -> btif_a2dp_source_stop_audio_req()"
* is called to stop the particular audio stream, and this happens right
* after the "BTIF_AV_CLEANUP_REQ_EVT -> btif_a2dp_source_shutdown()"
* processing during the shutdown of the Bluetooth stack.
*/
if (btif_a2dp_source_cb.cmd_msg_queue != NULL) {
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
} else if (pending_cmd == A2DP_CTRL_CMD_STOP ||
pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
BTIF_TRACE_DEBUG("media msg queue null, ack pending stop/suspend");
btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
}
btif_a2dp_source_cb.stats.session_end_us = time_get_os_boottime_us();
btif_a2dp_source_update_metrics();
btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
&btif_a2dp_source_cb.accumulated_stats);
}
void btif_a2dp_source_encoder_init(void) {
tBTIF_A2DP_SOURCE_ENCODER_INIT msg;
// Check to make sure the platform has 8 bits/byte since
// we're using that in frame size calculations now.
CHECK(CHAR_BIT == 8);
APPL_TRACE_DEBUG("%s", __func__);
bta_av_co_get_peer_params(&msg.peer_params);
btif_a2dp_source_encoder_init_req(&msg);
}
static void btif_a2dp_source_encoder_init_req(
tBTIF_A2DP_SOURCE_ENCODER_INIT* p_msg) {
tBTIF_A2DP_SOURCE_ENCODER_INIT* p_buf =
(tBTIF_A2DP_SOURCE_ENCODER_INIT*)osi_malloc(
sizeof(tBTIF_A2DP_SOURCE_ENCODER_INIT));
memcpy(p_buf, p_msg, sizeof(tBTIF_A2DP_SOURCE_ENCODER_INIT));
p_buf->hdr.event = BTIF_MEDIA_SOURCE_ENCODER_INIT;
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
}
void btif_media_send_reset_vendor_state() {
APPL_TRACE_ERROR("%s:", __func__);
BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR));
p_buf->event = BTIF_MEDIA_RESET_VS_STATE;
if (btif_a2dp_source_cb.cmd_msg_queue != NULL)
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
}
static void btif_a2dp_source_encoder_init_event(BT_HDR* p_msg) {
tBTIF_A2DP_SOURCE_ENCODER_INIT* p_encoder_init =
(tBTIF_A2DP_SOURCE_ENCODER_INIT*)p_msg;
APPL_TRACE_DEBUG("%s", __func__);
btif_a2dp_source_cb.encoder_interface = bta_av_co_get_encoder_interface();
if (btif_a2dp_source_cb.encoder_interface == NULL) {
APPL_TRACE_ERROR("%s: Cannot stream audio: no source encoder interface",
__func__);
return;
}
A2dpCodecConfig* a2dp_codec_config = bta_av_get_a2dp_current_codec();
if (a2dp_codec_config == nullptr) {
APPL_TRACE_ERROR("%s: Cannot stream audio: current codec is not set",
__func__);
return;
}
btif_a2dp_source_cb.encoder_interface->encoder_init(
&p_encoder_init->peer_params, a2dp_codec_config,
btif_a2dp_source_read_callback, btif_a2dp_source_enqueue_callback);
// Save a local copy of the encoder_interval_ms
btif_a2dp_source_cb.encoder_interval_ms =
btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms();
}
void btif_a2dp_source_encoder_user_config_update_req(
const btav_a2dp_codec_config_t& codec_user_config, const RawAddress& bd_addr) {
tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE* p_buf =
( tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE*)osi_malloc(
sizeof(tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE));
p_buf->user_config = codec_user_config;
p_buf->hdr.event = BTIF_MEDIA_SOURCE_ENCODER_USER_CONFIG_UPDATE;
p_buf->bd_addr = bd_addr;
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
}
static void btif_a2dp_source_encoder_user_config_update_event(BT_HDR* p_msg) {
tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE* p_user_config =
(tBTIF_A2DP_SOURCE_ENCODER_USER_CONFIG_UPDATE*)p_msg;
APPL_TRACE_DEBUG("%s", __func__);
if (!bta_av_co_set_codec_user_config(p_user_config->user_config, p_user_config->bd_addr)) {
APPL_TRACE_ERROR("%s: cannot update codec user configuration", __func__);
}
}
void btif_a2dp_source_feeding_update_req(
const btav_a2dp_codec_config_t& codec_audio_config) {
tBTIF_A2DP_AUDIO_FEEDING_UPDATE* p_buf =
(tBTIF_A2DP_AUDIO_FEEDING_UPDATE*)osi_malloc(
sizeof(tBTIF_A2DP_AUDIO_FEEDING_UPDATE));
p_buf->feeding_params = codec_audio_config;
p_buf->hdr.event = BTIF_MEDIA_AUDIO_FEEDING_UPDATE;
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
}
static void btif_a2dp_source_audio_feeding_update_event(BT_HDR* p_msg) {
tBTIF_A2DP_AUDIO_FEEDING_UPDATE* p_feeding =
(tBTIF_A2DP_AUDIO_FEEDING_UPDATE*)p_msg;
APPL_TRACE_DEBUG("%s", __func__);
if (!bta_av_co_set_codec_audio_config(p_feeding->feeding_params)) {
APPL_TRACE_ERROR("%s: cannot update codec audio feeding parameters",
__func__);
}
}
void btif_a2dp_source_on_idle(void) {
btif_media_send_reset_vendor_state();
if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
/* Make sure media task is stopped */
btif_a2dp_source_stop_audio_req();
}
void btif_a2dp_source_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
APPL_TRACE_EVENT("## ON A2DP SOURCE STOPPED ##");
tA2DP_CTRL_CMD pending_cmd = A2DP_CTRL_CMD_NONE;
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
pending_cmd = btif_ahim_get_pending_command(A2DP);
#else
pending_cmd = bluetooth::audio::a2dp::get_pending_command();
#endif
} else {
pending_cmd = btif_a2dp_control_get_pending_command();
}
if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
/* allow using this api for other than suspend */
if (p_av_suspend != NULL) {
if (p_av_suspend->status != BTA_AV_SUCCESS) {
APPL_TRACE_EVENT("AV STOP FAILED (%d)", p_av_suspend->status);
if (p_av_suspend->initiator) {
APPL_TRACE_WARNING("%s: A2DP stop request failed: status = %d",
__func__, p_av_suspend->status);
if ((pending_cmd == A2DP_CTRL_CMD_STOP) ||
(pending_cmd == A2DP_CTRL_CMD_SUSPEND)) {
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_FAILURE, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
#endif
} else {
btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
}
if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") &&
!strcmp(a2dp_hal_imp, "true")) {
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_reset_pending_command(A2DP);
#else
bluetooth::audio::a2dp::reset_pending_command();
#endif
} else {
btif_a2dp_pending_cmds_reset();
}
int index = ((p_av_suspend->hndl) & BTA_AV_HNDL_MSK) - 1;
RawAddress addr = btif_av_get_addr_by_index(index);
if (!addr.IsEmpty()) {
btif_dispatch_sm_event(BTIF_AV_DISCONNECT_REQ_EVT, (void *)addr.address,
sizeof(RawAddress));
BTIF_TRACE_DEBUG("%s: Disconnect for peer device on Start fail by Remote",__func__);
}
}
}
}
return;
}
}
BTIF_TRACE_DEBUG("%s: tx_flush: %d",__func__, btif_a2dp_source_cb.tx_flush);
/* ensure tx frames are immediately flushed */
if (btif_a2dp_source_cb.tx_flush == false)
btif_a2dp_source_cb.tx_flush = true;
/* request to stop media task */
if (!btif_a2dp_source_is_hal_v2_enabled() ||
(btif_a2dp_source_is_hal_v2_enabled() &&
#if AHIM_ENABLED
btif_ahim_get_session_type(A2DP) ==
#else
bluetooth::audio::a2dp::get_session_type() ==
#endif
SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH)) {
btif_a2dp_source_audio_tx_flush_req();
BTIF_TRACE_DEBUG("%s: stop audio as it is SW session",__func__);
btif_a2dp_source_stop_audio_req();
}
/* once stream is fully stopped we will ack back */
}
void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
APPL_TRACE_EVENT("## ON A2DP SOURCE SUSPENDED ##");
tA2DP_CTRL_CMD pending_cmd = A2DP_CTRL_CMD_NONE;
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
pending_cmd = btif_ahim_get_pending_command(A2DP);
#else
pending_cmd = bluetooth::audio::a2dp::get_pending_command();
#endif
} else {
pending_cmd = btif_a2dp_control_get_pending_command();
}
if (btif_a2dp_source_state == BTIF_A2DP_SOURCE_STATE_OFF) return;
/* check for status failures */
if (p_av_suspend != NULL) {
if (p_av_suspend->status != BTA_AV_SUCCESS) {
if (p_av_suspend->initiator) {
APPL_TRACE_WARNING("%s: A2DP suspend request failed: status = %d",
__func__, p_av_suspend->status);
if ((pending_cmd == A2DP_CTRL_CMD_STOP) ||
(pending_cmd == A2DP_CTRL_CMD_SUSPEND)) {
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_FAILURE, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
#endif
} else {
btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
}
if (property_get("persist.vendor.bt.a2dp.hal.implementation", a2dp_hal_imp, "false") &&
!strcmp(a2dp_hal_imp, "true")) {
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_reset_pending_command(A2DP);
#else
bluetooth::audio::a2dp::reset_pending_command();
#endif
} else {
btif_a2dp_pending_cmds_reset();
}
int index = ((p_av_suspend->hndl) & BTA_AV_HNDL_MSK) - 1;
RawAddress addr = btif_av_get_addr_by_index(index);
if (!addr.IsEmpty()) {
btif_dispatch_sm_event(BTIF_AV_DISCONNECT_REQ_EVT, (void *)addr.address,
sizeof(RawAddress));
BTIF_TRACE_DEBUG("%s: Disconnect for peer device on Start fail by Remote",__func__);
}
}
}
}
}
}
/* once stream is fully stopped we will ack back */
BTIF_TRACE_DEBUG("%s: tx_flush: %d",__func__, btif_a2dp_source_cb.tx_flush);
/* ensure tx frames are immediately flushed */
if (btif_a2dp_source_cb.tx_flush == false)
btif_a2dp_source_cb.tx_flush = true;
/* stop timer tick */
/* request to stop media task */
if (!btif_a2dp_source_is_hal_v2_enabled() ||
(btif_a2dp_source_is_hal_v2_enabled() &&
#if AHIM_ENABLED
btif_ahim_get_session_type(A2DP) ==
#else
bluetooth::audio::a2dp::get_session_type() ==
#endif
SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH)) {
BTIF_TRACE_DEBUG("%s: stop audio as it is SW session",__func__);
btif_a2dp_source_stop_audio_req();
}
}
/* when true media task discards any tx frames */
void btif_a2dp_source_set_tx_flush(bool enable) {
APPL_TRACE_EVENT("## DROP TX %d ##", enable);
btif_a2dp_source_cb.tx_flush = enable;
}
static void btif_a2dp_source_audio_tx_start_event(void) {
APPL_TRACE_DEBUG(
"%s media_alarm is %srunning, streaming %s", __func__,
alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
btif_a2dp_source_is_streaming() ? "true" : "false");
/* Reset the media feeding state */
CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
btif_a2dp_source_cb.encoder_interface->feeding_reset();
APPL_TRACE_EVENT(
"starting timer %dms",
btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms());
alarm_free(btif_a2dp_source_cb.media_alarm);
btif_a2dp_source_cb.media_alarm =
alarm_new_periodic("btif.a2dp_source_media_alarm");
if (btif_a2dp_source_cb.media_alarm == NULL) {
LOG_ERROR(LOG_TAG, "%s unable to allocate media alarm", __func__);
return;
}
alarm_set(btif_a2dp_source_cb.media_alarm,
btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms(),
btif_a2dp_source_alarm_cb, NULL);
}
static void btif_a2dp_source_audio_tx_stop_event(void) {
APPL_TRACE_DEBUG(
"%s media_alarm is %srunning, streaming %s", __func__,
alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
btif_a2dp_source_is_streaming() ? "true" : "false");
uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
uint16_t event;
tA2DP_CTRL_CMD pending_cmd = A2DP_CTRL_CMD_NONE;
// Keep track of audio data still left in the pipe
if (btif_a2dp_source_is_hal_v2_supported()) {
btif_a2dp_control_log_bytes_read(
#if AHIM_ENABLED
btif_ahim_read(p_buf, sizeof(p_buf)));
#else
bluetooth::audio::a2dp::read(p_buf, sizeof(p_buf)));
#endif
} else {
btif_a2dp_control_log_bytes_read(
UIPC_Read(UIPC_CH_ID_AV_AUDIO, &event, p_buf, sizeof(p_buf)));
}
/* Stop the timer first */
alarm_free(btif_a2dp_source_cb.media_alarm);
btif_a2dp_source_cb.media_alarm = NULL;
if (!btif_a2dp_source_is_hal_v2_supported()) {
UIPC_Close(UIPC_CH_ID_AV_AUDIO);
}
/*
* Try to send acknowldegment once the media stream is
* stopped. This will make sure that the A2DP HAL layer is
* un-blocked on wait for acknowledgment for the sent command.
* This resolves a corner cases AVDTP SUSPEND collision
* when the DUT and the remote device issue SUSPEND simultaneously
* and due to the processing of the SUSPEND request from the remote,
* the media path is torn down. If the A2DP HAL happens to wait
* for ACK for the initiated SUSPEND, it would never receive it casuing
* a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed
* to get the ACK for any pending command in such cases.
*/
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
pending_cmd = btif_ahim_get_pending_command(A2DP);
#else
pending_cmd = bluetooth::audio::a2dp::get_pending_command();
#endif
} else {
pending_cmd = btif_a2dp_control_get_pending_command();
}
if (pending_cmd == A2DP_CTRL_CMD_SUSPEND ||
pending_cmd == A2DP_CTRL_CMD_STOP) {
APPL_TRACE_DEBUG("%s, Ack for pending Stop/Suspend", __func__);
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
#endif
} else {
btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
}
} else if (pending_cmd == A2DP_CTRL_CMD_START) {
BTIF_TRACE_ERROR("Ack Pending Start while Disconnect in Progress");
if (btif_a2dp_source_is_hal_v2_supported()) {
#if AHIM_ENABLED
btif_ahim_ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
#endif
} else {
btif_a2dp_command_ack(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS);
}
} else {
BTIF_TRACE_ERROR("Invalid cmd pending for ack");
}
/* audio engine stopped, reset tx suspended flag */
btif_a2dp_source_cb.tx_flush = false;
/* Reset the media feeding state */
if (btif_a2dp_source_cb.encoder_interface != NULL)
btif_a2dp_source_cb.encoder_interface->feeding_reset();
}
static void btif_a2dp_source_alarm_cb(UNUSED_ATTR void* context) {
APPL_TRACE_DEBUG("%s:", __func__);
thread_post(btif_a2dp_source_cb.worker_thread,
btif_a2dp_source_audio_handle_timer, NULL);
}
static void btif_a2dp_source_audio_handle_timer(UNUSED_ATTR void* context) {
uint64_t timestamp_us = time_get_os_boottime_us();
int curr_idx = btif_av_get_latest_device_idx_to_start();
log_tstamps_us("A2DP Source tx timer", timestamp_us);
if (alarm_is_scheduled(btif_a2dp_source_cb.media_alarm)) {
CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
size_t transmit_queue_length =
fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
#ifndef OS_GENERIC
ATRACE_INT("btif TX queue", transmit_queue_length);
#endif
if (btif_a2dp_source_cb.encoder_interface->set_transmit_queue_length !=
NULL) {
btif_a2dp_source_cb.encoder_interface->set_transmit_queue_length(
transmit_queue_length);
}
btif_a2dp_source_cb.encoder_interface->send_frames(timestamp_us);
if (btif_av_check_flag_remote_suspend(curr_idx) || btif_a2dp_source_cb.tx_flush) {
APPL_TRACE_ERROR("Don't signal data ready BTU task since remote suspended or tx_flush = %d", btif_a2dp_source_cb.tx_flush);
} else {
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
}
update_scheduling_stats(&btif_a2dp_source_cb.stats.tx_queue_enqueue_stats,
timestamp_us,
btif_a2dp_source_cb.encoder_interval_ms * 1000);
} else {
APPL_TRACE_ERROR("ERROR Media task Scheduled after Suspend");
}
}
static uint32_t btif_a2dp_source_read_callback(uint8_t* p_buf, uint32_t len) {
uint16_t event;
uint32_t bytes_read = 0;
if (btif_a2dp_source_is_hal_v2_supported()) {
#if AHIM_ENABLED
bytes_read = btif_ahim_read(p_buf, len);
#else
bytes_read = bluetooth::audio::a2dp::read(p_buf, len);
#endif
} else {
bytes_read = UIPC_Read(UIPC_CH_ID_AV_AUDIO, &event, p_buf, len);
}
if (bytes_read < len) {
LOG_WARN(LOG_TAG, "%s: UNDERFLOW: ONLY READ %d BYTES OUT OF %d", __func__,
bytes_read, len);
btif_a2dp_source_cb.stats.media_read_total_underflow_bytes +=
(len - bytes_read);
btif_a2dp_source_cb.stats.media_read_total_underflow_count++;
btif_a2dp_source_cb.stats.media_read_last_underflow_us =
time_get_os_boottime_us();
}
return bytes_read;
}
static bool btif_a2dp_source_enqueue_callback(BT_HDR* p_buf, size_t frames_n,
uint32_t bytes_read) {
uint64_t now_us = time_get_os_boottime_us();
btif_a2dp_control_log_bytes_read(bytes_read);
int curr_idx = btif_av_get_latest_device_idx_to_start();
APPL_TRACE_DEBUG("%s: tx_flush: %d", __func__, btif_a2dp_source_cb.tx_flush);
/* Check if timer was stopped (media task stopped) */
if (!alarm_is_scheduled(btif_a2dp_source_cb.media_alarm)) {
osi_free(p_buf);
return false;
}
/* Check if the transmission queue has been flushed */
if (btif_a2dp_source_cb.tx_flush || btif_av_check_flag_remote_suspend(curr_idx)) {
LOG_DEBUG(LOG_TAG, "%s: tx suspended %d or remote suspended, discarded frame", __func__, btif_a2dp_source_cb.tx_flush);
btif_a2dp_source_cb.stats.tx_queue_total_flushed_messages +=
fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
btif_a2dp_source_cb.stats.tx_queue_last_flushed_us = now_us;
fixed_queue_flush(btif_a2dp_source_cb.tx_audio_queue, osi_free);
osi_free(p_buf);
return false;
}
// Check for TX queue overflow
// TODO: Using frames_n here is probably wrong: should be "+ 1" instead.
if (fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue) + frames_n >
btif_a2dp_source_dynamic_audio_buffer_size) {
LOG_DEBUG(LOG_TAG, "%s: TX queue buffer size now=%u adding=%u max=%d",
__func__,
(uint32_t)fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue),
(uint32_t)frames_n, btif_a2dp_source_dynamic_audio_buffer_size);
// Keep track of drop-outs
btif_a2dp_source_cb.stats.tx_queue_dropouts++;
btif_a2dp_source_cb.stats.tx_queue_last_dropouts_us = now_us;
// Flush all queued buffers
size_t drop_n = fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
btif_a2dp_source_cb.stats.tx_queue_max_dropped_messages = std::max(
drop_n, btif_a2dp_source_cb.stats.tx_queue_max_dropped_messages);
while (fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue)) {
btif_a2dp_source_cb.stats.tx_queue_total_dropped_messages++;
osi_free(fixed_queue_try_dequeue(btif_a2dp_source_cb.tx_audio_queue));
}
// Request RSSI and Failed Contact Counter for log purposes if we had to
// flush buffers.
RawAddress peer_bda;
btif_av_get_active_peer_addr(&peer_bda);
tBTM_STATUS status = BTM_ReadRSSI(peer_bda, btm_read_rssi_cb);
if (status != BTM_CMD_STARTED) {
LOG_DEBUG(LOG_TAG, "%s: Cannot read RSSI: status %d", __func__, status);
}
status = BTM_ReadFailedContactCounter(peer_bda,
btm_read_failed_contact_counter_cb);
if (status != BTM_CMD_STARTED) {
LOG_DEBUG(LOG_TAG, "%s: Cannot read Failed Contact Counter: status %d",
__func__, status);
}
status = BTM_ReadAutomaticFlushTimeout(peer_bda,
btm_read_automatic_flush_timeout_cb);
if (status != BTM_CMD_STARTED) {
LOG_DEBUG(LOG_TAG, "%s: Cannot read Automatic Flush Timeout: status %d",
__func__, status);
}
status =
BTM_ReadTxPower(peer_bda, BT_TRANSPORT_BR_EDR, btm_read_tx_power_cb);
if (status != BTM_CMD_STARTED) {
LOG_DEBUG(LOG_TAG, "%s: Cannot read Tx Power: status %d", __func__,
status);
}
}
APPL_TRACE_DEBUG("%s: Update the statistics and enquue the packets.", __func__);
/* Update the statistics */
btif_a2dp_source_cb.stats.tx_queue_total_frames += frames_n;
btif_a2dp_source_cb.stats.tx_queue_max_frames_per_packet = std::max(
frames_n, btif_a2dp_source_cb.stats.tx_queue_max_frames_per_packet);
CHECK(btif_a2dp_source_cb.encoder_interface != NULL);
fixed_queue_enqueue(btif_a2dp_source_cb.tx_audio_queue, p_buf);
return true;
}
static void btif_a2dp_source_audio_tx_flush_event(UNUSED_ATTR BT_HDR* p_msg) {
/* Flush all enqueued audio buffers (encoded) */
APPL_TRACE_DEBUG("%s", __func__);
if (btif_a2dp_source_cb.encoder_interface != NULL)
btif_a2dp_source_cb.encoder_interface->feeding_flush();
btif_a2dp_source_cb.stats.tx_queue_total_flushed_messages +=
fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue);
btif_a2dp_source_cb.stats.tx_queue_last_flushed_us =
time_get_os_boottime_us();
fixed_queue_flush(btif_a2dp_source_cb.tx_audio_queue, osi_free);
if (!btif_a2dp_source_is_hal_v2_supported()) {
UIPC_Ioctl(UIPC_CH_ID_AV_AUDIO, UIPC_REQ_RX_FLUSH, NULL);
}
}
static bool btif_a2dp_source_audio_tx_flush_req(void) {
BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
p_buf->event = BTIF_MEDIA_AUDIO_TX_FLUSH;
/*
* Explicitly check whether the btif_a2dp_source_cb.cmd_msg_queue is not
* NULL to avoid a race condition during shutdown of the Bluetooth stack.
* This race condition is triggered when A2DP audio is streaming on
* shutdown:
* "btif_a2dp_source_on_stopped() -> btif_a2dp_source_audio_tx_flush_req()"
* is called to stop the particular audio stream, and this happens right
* after the "BTIF_AV_CLEANUP_REQ_EVT -> btif_a2dp_source_shutdown()"
* processing during the shutdown of the Bluetooth stack.
*/
if (btif_a2dp_source_cb.cmd_msg_queue != NULL)
fixed_queue_enqueue(btif_a2dp_source_cb.cmd_msg_queue, p_buf);
return true;
}
BT_HDR* btif_a2dp_source_audio_readbuf(void) {
uint64_t now_us = time_get_os_boottime_us();
BT_HDR* p_buf =
(BT_HDR*)fixed_queue_try_dequeue(btif_a2dp_source_cb.tx_audio_queue);
APPL_TRACE_DEBUG("%s:", __func__);
btif_a2dp_source_cb.stats.tx_queue_total_readbuf_calls++;
btif_a2dp_source_cb.stats.tx_queue_last_readbuf_us = now_us;
if (p_buf != NULL) {
APPL_TRACE_DEBUG("%s: p_buf is not null, updating queue statistics.", __func__);
// Update the statistics
update_scheduling_stats(&btif_a2dp_source_cb.stats.tx_queue_dequeue_stats,
now_us,
btif_a2dp_source_cb.encoder_interval_ms * 1000);
}
return p_buf;
}
static void log_tstamps_us(const char* comment, uint64_t timestamp_us) {
static uint64_t prev_us = 0;
APPL_TRACE_DEBUG("[%s] ts %08llu, diff : %08llu, queue sz %d", comment,
timestamp_us, timestamp_us - prev_us,
fixed_queue_length(btif_a2dp_source_cb.tx_audio_queue));
prev_us = timestamp_us;
}
static void update_scheduling_stats(scheduling_stats_t* stats, uint64_t now_us,
uint64_t expected_delta) {
uint64_t last_us = stats->last_update_us;
stats->total_updates++;
stats->last_update_us = now_us;
if (last_us == 0) return; // First update: expected delta doesn't apply
uint64_t deadline_us = last_us + expected_delta;
if (deadline_us < now_us) {
// Overdue scheduling
uint64_t delta_us = now_us - deadline_us;
// Ignore extreme outliers
if (delta_us < 10 * expected_delta) {
stats->max_overdue_scheduling_delta_us =
std::max(delta_us, stats->max_overdue_scheduling_delta_us);
stats->total_overdue_scheduling_delta_us += delta_us;
stats->overdue_scheduling_count++;
stats->total_scheduling_time_us += now_us - last_us;
}
} else if (deadline_us > now_us) {
// Premature scheduling
uint64_t delta_us = deadline_us - now_us;
// Ignore extreme outliers
if (delta_us < 10 * expected_delta) {
stats->max_premature_scheduling_delta_us =
std::max(delta_us, stats->max_premature_scheduling_delta_us);
stats->total_premature_scheduling_delta_us += delta_us;
stats->premature_scheduling_count++;
stats->total_scheduling_time_us += now_us - last_us;
}
} else {
// On-time scheduling
stats->exact_scheduling_count++;
stats->total_scheduling_time_us += now_us - last_us;
}
}
void btif_a2dp_source_debug_dump(int fd) {
btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
&btif_a2dp_source_cb.accumulated_stats);
uint64_t now_us = time_get_os_boottime_us();
btif_media_stats_t* accumulated_stats =
&btif_a2dp_source_cb.accumulated_stats;
scheduling_stats_t* enqueue_stats =
&accumulated_stats->tx_queue_enqueue_stats;
scheduling_stats_t* dequeue_stats =
&accumulated_stats->tx_queue_dequeue_stats;
size_t ave_size;
uint64_t ave_time_us;
dprintf(fd, "\nA2DP State:\n");
dprintf(fd, " TxQueue:\n");
dprintf(fd,
" Counts (enqueue/dequeue/readbuf) : %zu / "
"%zu / %zu\n",
enqueue_stats->total_updates, dequeue_stats->total_updates,
accumulated_stats->tx_queue_total_readbuf_calls);
dprintf(
fd,
" Last update time ago in ms (enqueue/dequeue/readbuf) : %llu / %llu "
"/ %llu\n",
(enqueue_stats->last_update_us > 0)
? (unsigned long long)(now_us - enqueue_stats->last_update_us) / 1000
: 0,
(dequeue_stats->last_update_us > 0)
? (unsigned long long)(now_us - dequeue_stats->last_update_us) / 1000
: 0,
(accumulated_stats->tx_queue_last_readbuf_us > 0)
? (unsigned long long)(now_us -
accumulated_stats->tx_queue_last_readbuf_us) /
1000
: 0);
ave_size = 0;
if (enqueue_stats->total_updates != 0)
ave_size =
accumulated_stats->tx_queue_total_frames / enqueue_stats->total_updates;
dprintf(fd,
" Frames per packet (total/max/ave) : %zu / "
"%zu / %zu\n",
accumulated_stats->tx_queue_total_frames,
accumulated_stats->tx_queue_max_frames_per_packet, ave_size);
dprintf(fd,
" Counts (flushed/dropped/dropouts) : %zu / "
"%zu / %zu\n",
accumulated_stats->tx_queue_total_flushed_messages,
accumulated_stats->tx_queue_total_dropped_messages,
accumulated_stats->tx_queue_dropouts);
dprintf(fd,
" Counts (max dropped) : %zu\n",
accumulated_stats->tx_queue_max_dropped_messages);
dprintf(
fd,
" Last update time ago in ms (flushed/dropped) : %llu / "
"%llu\n",
(accumulated_stats->tx_queue_last_flushed_us > 0)
? (unsigned long long)(now_us -
accumulated_stats->tx_queue_last_flushed_us) /
1000
: 0,
(accumulated_stats->tx_queue_last_dropouts_us > 0)
? (unsigned long long)(now_us -
accumulated_stats->tx_queue_last_dropouts_us) /
1000
: 0);
dprintf(fd,
" Counts (underflow) : %zu\n",
accumulated_stats->media_read_total_underflow_count);
dprintf(fd,
" Bytes (underflow) : %zu\n",
accumulated_stats->media_read_total_underflow_bytes);
dprintf(fd,
" Last update time ago in ms (underflow) : %llu\n",
(accumulated_stats->media_read_last_underflow_us > 0)
? (unsigned long long)(now_us -
accumulated_stats
->media_read_last_underflow_us) /
1000
: 0);
//
// TxQueue enqueue stats
//
dprintf(
fd,
" Enqueue deviation counts (overdue/premature) : %zu / %zu\n",
enqueue_stats->overdue_scheduling_count,
enqueue_stats->premature_scheduling_count);
ave_time_us = 0;
if (enqueue_stats->overdue_scheduling_count != 0) {
ave_time_us = enqueue_stats->total_overdue_scheduling_delta_us /
enqueue_stats->overdue_scheduling_count;
}
dprintf(
fd,
" Enqueue overdue scheduling time in ms (total/max/ave) : %llu / %llu "
"/ %llu\n",
(unsigned long long)enqueue_stats->total_overdue_scheduling_delta_us /
1000,
(unsigned long long)enqueue_stats->max_overdue_scheduling_delta_us / 1000,
(unsigned long long)ave_time_us / 1000);
ave_time_us = 0;
if (enqueue_stats->premature_scheduling_count != 0) {
ave_time_us = enqueue_stats->total_premature_scheduling_delta_us /
enqueue_stats->premature_scheduling_count;
}
dprintf(
fd,
" Enqueue premature scheduling time in ms (total/max/ave) : %llu / %llu "
"/ %llu\n",
(unsigned long long)enqueue_stats->total_premature_scheduling_delta_us /
1000,
(unsigned long long)enqueue_stats->max_premature_scheduling_delta_us /
1000,
(unsigned long long)ave_time_us / 1000);
//
// TxQueue dequeue stats
//
dprintf(
fd,
" Dequeue deviation counts (overdue/premature) : %zu / %zu\n",
dequeue_stats->overdue_scheduling_count,
dequeue_stats->premature_scheduling_count);
ave_time_us = 0;
if (dequeue_stats->overdue_scheduling_count != 0) {
ave_time_us = dequeue_stats->total_overdue_scheduling_delta_us /
dequeue_stats->overdue_scheduling_count;
}
dprintf(
fd,
" Dequeue overdue scheduling time in ms (total/max/ave) : %llu / %llu "
"/ %llu\n",
(unsigned long long)dequeue_stats->total_overdue_scheduling_delta_us /
1000,
(unsigned long long)dequeue_stats->max_overdue_scheduling_delta_us / 1000,
(unsigned long long)ave_time_us / 1000);
ave_time_us = 0;
if (dequeue_stats->premature_scheduling_count != 0) {
ave_time_us = dequeue_stats->total_premature_scheduling_delta_us /
dequeue_stats->premature_scheduling_count;
}
dprintf(
fd,
" Dequeue premature scheduling time in ms (total/max/ave) : %llu / %llu "
"/ %llu\n",
(unsigned long long)dequeue_stats->total_premature_scheduling_delta_us /
1000,
(unsigned long long)dequeue_stats->max_premature_scheduling_delta_us /
1000,
(unsigned long long)ave_time_us / 1000);
//
// Codec-specific stats
//
A2dpCodecs* a2dp_codecs = bta_av_get_a2dp_codecs();
if (a2dp_codecs != nullptr) {
a2dp_codecs->debug_codec_dump(fd);
}
}
void btif_a2dp_source_update_metrics(void) {
btif_media_stats_t* stats = &btif_a2dp_source_cb.stats;
scheduling_stats_t* enqueue_stats = &stats->tx_queue_enqueue_stats;
A2dpSessionMetrics metrics;
// session_start_us is 0 when btif_a2dp_source_start_audio_req() is not called
// mark the metric duration as invalid (-1) in this case
if (stats->session_start_us != 0) {
int64_t session_end_us = stats->session_end_us == 0
? time_get_os_boottime_us()
: stats->session_end_us;
if (static_cast(session_end_us) > stats->session_start_us) {
metrics.audio_duration_ms =
(session_end_us - stats->session_start_us) / 1000;
}
}
if (enqueue_stats->total_updates > 1) {
metrics.media_timer_min_ms =
btif_a2dp_source_cb.encoder_interval_ms -
(enqueue_stats->max_premature_scheduling_delta_us / 1000);
metrics.media_timer_max_ms =
btif_a2dp_source_cb.encoder_interval_ms +
(enqueue_stats->max_overdue_scheduling_delta_us / 1000);
metrics.total_scheduling_count = enqueue_stats->overdue_scheduling_count +
enqueue_stats->premature_scheduling_count +
enqueue_stats->exact_scheduling_count;
if (metrics.total_scheduling_count > 0) {
metrics.media_timer_avg_ms = enqueue_stats->total_scheduling_time_us /
(1000 * metrics.total_scheduling_count);
}
metrics.buffer_overruns_max_count = stats->tx_queue_max_dropped_messages;
metrics.buffer_overruns_total = stats->tx_queue_total_dropped_messages;
metrics.buffer_underruns_count = stats->media_read_total_underflow_count;
metrics.buffer_underruns_average = 0;
if (metrics.buffer_underruns_count > 0) {
metrics.buffer_underruns_average =
stats->media_read_total_underflow_bytes /
metrics.buffer_underruns_count;
}
}
BluetoothMetricsLogger::GetInstance()->LogA2dpSession(metrics);
}
void btif_a2dp_source_set_dynamic_audio_buffer_size(
uint8_t dynamic_audio_buffer_size) {
btif_a2dp_source_dynamic_audio_buffer_size = dynamic_audio_buffer_size;
}
static void btm_read_rssi_cb(void* data) {
if (data == nullptr) {
LOG_ERROR(LOG_TAG, "%s Read RSSI request timed out", __func__);
return;
}
tBTM_RSSI_RESULT* result = (tBTM_RSSI_RESULT*)data;
if (result->status != BTM_SUCCESS) {
LOG_ERROR(LOG_TAG, "%s unable to read remote RSSI (status %d)", __func__,
result->status);
return;
}
LOG_WARN(LOG_TAG, "%s device: %s, rssi: %d", __func__,
result->rem_bda.ToString().c_str(), result->rssi);
}
static void btm_read_failed_contact_counter_cb(void* data) {
if (data == nullptr) {
LOG_ERROR(LOG_TAG, "%s Read Failed Contact Counter request timed out",
__func__);
return;
}
tBTM_FAILED_CONTACT_COUNTER_RESULT* result =
(tBTM_FAILED_CONTACT_COUNTER_RESULT*)data;
if (result->status != BTM_SUCCESS) {
LOG_ERROR(LOG_TAG, "%s unable to read Failed Contact Counter (status %d)",
__func__, result->status);
return;
}
LOG_WARN(LOG_TAG, "%s device: %s, Failed Contact Counter: %u", __func__,
result->rem_bda.ToString().c_str(), result->failed_contact_counter);
}
static void btm_read_automatic_flush_timeout_cb(void* data) {
if (data == nullptr) {
LOG_ERROR(LOG_TAG, "%s Read Automatic Flush Timeout request timed out",
__func__);
return;
}
tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT* result =
(tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT*)data;
if (result->status != BTM_SUCCESS) {
LOG_ERROR(LOG_TAG, "%s unable to read Automatic Flush Timeout (status %d)",
__func__, result->status);
return;
}
LOG_WARN(LOG_TAG, "%s device: %s, Automatic Flush Timeout: %u", __func__,
result->rem_bda.ToString().c_str(), result->automatic_flush_timeout);
}
static void btm_read_tx_power_cb(void* data) {
if (data == nullptr) {
LOG_ERROR(LOG_TAG, "%s Read Tx Power request timed out", __func__);
return;
}
tBTM_TX_POWER_RESULT* result = (tBTM_TX_POWER_RESULT*)data;
if (result->status != BTM_SUCCESS) {
LOG_ERROR(LOG_TAG, "%s unable to read Tx Power (status %d)", __func__,
result->status);
return;
}
LOG_WARN(LOG_TAG, "%s device: %s, Tx Power: %d", __func__,
result->rem_bda.ToString().c_str(), result->tx_power);
}
bool btif_a2dp_source_is_hal_v2_enabled(void) {
#if AHIM_ENABLED
return btif_ahim_is_hal_2_0_enabled();
#else
return bluetooth::audio::a2dp::is_hal_2_0_enabled();
#endif
}
bool btif_a2dp_source_is_hal_v2_supported(void) {
#if AHIM_ENABLED
return btif_ahim_is_hal_2_0_supported();
#else
return bluetooth::audio::a2dp::is_hal_2_0_supported();
#endif
}
bool btif_a2dp_source_start_session(const RawAddress& peer_address) {
LOG_ERROR(LOG_TAG, "%s: peer_address=%s state=%d", __func__,
peer_address.ToString().c_str(), btif_a2dp_source_state);
tBTA_AV_HNDL hndl = btif_av_get_hndl_by_addr(peer_address);;
if (btif_a2dp_source_state != BTIF_A2DP_SOURCE_STATE_RUNNING) {
LOG_ERROR(LOG_TAG, "%s: A2DP Source media task is not running", __func__);
return false;
}
if (btif_a2dp_source_is_hal_v2_supported()) {
APPL_TRACE_EVENT("%s calling ## init ##", __func__);
#if AHIM_ENABLED
btif_ahim_init_hal(btif_a2dp_source_cb.worker_thread, A2DP);
#else
bluetooth::audio::a2dp::init(btif_a2dp_source_cb.worker_thread);
#endif
}
btif_a2dp_source_setup_codec(hndl);
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_start_session(A2DP);
if (btif_ahim_get_session_type(A2DP) ==
#else
bluetooth::audio::a2dp::start_session();
if (bluetooth::audio::a2dp::get_session_type() ==
#endif
SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
APPL_TRACE_EVENT("%s Freeing queue from previous session", __func__);
fixed_queue_flush(btif_a2dp_source_cb.tx_audio_queue, osi_free);
}
}
btif_a2dp_update_sink_latency_change();
return true;
}
bool btif_a2dp_source_is_restart_session_needed() {
bool restart_session = false;
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
restart_session = btif_ahim_is_restart_session_needed(A2DP);
#else
restart_session = bluetooth::audio::a2dp::is_restart_session_needed();
#endif
}
APPL_TRACE_EVENT("%s :: restart_session %d", __func__, restart_session);
return restart_session;
}
bool btif_a2dp_source_restart_session(const RawAddress& old_peer_address,
const RawAddress& new_peer_address) {
#if AHIM_ENABLED
bool is_streaming = btif_ahim_is_streaming();
SessionType session_type = btif_ahim_get_session_type(A2DP);
#else
bool is_streaming = bluetooth::audio::a2dp::is_streaming();
SessionType session_type = bluetooth::audio::a2dp::get_session_type();
#endif
LOG_ERROR(LOG_TAG,
"%s: old_peer_address=%s new_peer_address=%s is_streaming=%d "
"state=%d", __func__, old_peer_address.ToString().c_str(),
new_peer_address.ToString().c_str(), is_streaming, btif_a2dp_source_state);
CHECK(!new_peer_address.IsEmpty());
// Must stop first the audio streaming
if (is_streaming &&
session_type == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
APPL_TRACE_EVENT("%s stop audio as old session is SW and streaming", __func__);
btif_a2dp_source_stop_audio_req();
}
// If the old active peer was valid or if session is not
// unknown, end the old session.
// Otherwise, time to startup the A2DP Source processing.
if (!old_peer_address.IsEmpty() ||
session_type != SessionType::UNKNOWN) {
btif_a2dp_source_end_session(old_peer_address);
}
btif_av_set_offload_status();
// Start the session.
btif_a2dp_source_start_session(new_peer_address);
// No need to start the audio here as on remote start ack
// it will start the audio
return true;
}
bool btif_a2dp_source_end_session(const RawAddress& peer_address) {
LOG_ERROR(LOG_TAG, "%s: peer_address=%s state=%d", __func__,
peer_address.ToString().c_str(), btif_a2dp_source_state);
if (!btif_av_is_split_a2dp_enabled()) {
BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
}
/* request to stop media task */
if (!btif_a2dp_source_is_hal_v2_enabled() ||
(btif_a2dp_source_is_hal_v2_enabled() &&
#if AHIM_ENABLED
btif_ahim_get_session_type(A2DP) ==
#else
bluetooth::audio::a2dp::get_session_type() ==
#endif
SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH)) {
if (btif_a2dp_source_cb.tx_flush == false)
btif_a2dp_source_cb.tx_flush = true;
btif_a2dp_source_audio_tx_flush_req();
BTIF_TRACE_DEBUG("%s: stop audio as it is SW session",__func__);
btif_a2dp_source_stop_audio_req();
}
if (btif_a2dp_source_is_hal_v2_enabled()) {
#if AHIM_ENABLED
btif_ahim_end_session(A2DP);
#else
bluetooth::audio::a2dp::end_session();
#endif
}
return true;
}
void btif_a2dp_update_sink_latency_change() {
APPL_TRACE_EVENT("%s", __func__);
if (btif_a2dp_source_is_hal_v2_enabled()) {
uint16_t sink_latency;
int idx = btif_av_get_current_playing_dev_idx();
if (btif_ba_get_state() > BTIF_BA_STATE_IDLE_AUDIO_NS) { // IsBAEnabled
sink_latency = btif_get_ba_latency();
} else { // Split OR Non-Split A2DP OR Hearing AID
sink_latency = btif_av_get_audio_delay(idx);
}
APPL_TRACE_EVENT("%s latency/delay value %d", __func__, sink_latency);
#if AHIM_ENABLED
btif_ahim_set_remote_delay(sink_latency, A2DP);
#else
bluetooth::audio::a2dp::set_remote_delay(sink_latency);
#endif
} else {
btif_a2dp_audio_send_sink_latency();
}
}
void btif_a2dp_source_command_ack(tA2DP_CTRL_CMD cmd, tA2DP_CTRL_ACK status) {
switch (cmd) {
case A2DP_CTRL_CMD_START:
#if AHIM_ENABLED
btif_ahim_ack_stream_started(status, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_started(status);
#endif
break;
case A2DP_CTRL_CMD_SUSPEND:
case A2DP_CTRL_CMD_STOP:
#if AHIM_ENABLED
btif_ahim_ack_stream_suspended(status, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_suspended(status);
#endif
break;
default:
break;
}
}
void btif_a2dp_source_process_request(tA2DP_CTRL_CMD cmd) {
tA2DP_CTRL_ACK status = A2DP_CTRL_ACK_FAILURE;
bool start_audio = false;
// update the pending command
#if AHIM_ENABLED
btif_ahim_update_pending_command(cmd, A2DP);
#else
bluetooth::audio::a2dp::update_pending_command(cmd);
#endif
switch (cmd) {
case A2DP_CTRL_CMD_START:
{
/*
* Don't send START request to stack while we are in a call.
* Some headsets such as "Sony MW600", don't allow AVDTP START
* while in a call, and respond with BAD_STATE.
*/
bool reset_remote_start = false;
bool remote_start_flag = false;
int remote_start_idx = btif_max_av_clients;
int latest_playing_idx = btif_max_av_clients;
if (!bta_sys_is_register(BTA_ID_AV)) {
APPL_TRACE_ERROR("AV is disabled, return disc in progress");
status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;
break;
}
if (!bluetooth::headset::btif_hf_is_call_vr_idle()) {
status = A2DP_CTRL_ACK_INCALL_FAILURE;
break;
}
if (btif_ba_is_active())
{
ba_send_message(BTIF_BA_AUDIO_START_REQ_EVT, 0, NULL, false);
status = A2DP_CTRL_ACK_PENDING;
break;
}
remote_start_idx = btif_get_is_remote_started_idx();
latest_playing_idx = btif_av_get_latest_device_idx_to_start();
remote_start_flag = btif_av_is_remote_started_set(latest_playing_idx);
if (btif_a2dp_source_is_remote_start()) {
reset_remote_start = false;
APPL_TRACE_DEBUG("%s: remote started idx = %d latest playing = %d",
__func__, remote_start_idx, latest_playing_idx);
#if (TWS_ENABLED == TRUE)
if (btif_av_current_device_is_tws() &&
btif_av_is_idx_tws_device(remote_start_idx)) {
APPL_TRACE_DEBUG("%s:Remote started by TWS+ device, force cancel",
__func__);
reset_remote_start = true;
}
if (!reset_remote_start && (remote_start_idx < btif_max_av_clients) &&
#else
if ((remote_start_idx < btif_max_av_clients) &&
#endif
((latest_playing_idx < btif_max_av_clients &&
latest_playing_idx != remote_start_idx) ||
btif_av_is_local_started_on_other_idx(remote_start_idx))) {
APPL_TRACE_WARNING("%s: Already playing on other index, \
don't cancel remote start timer",__func__);
status = A2DP_CTRL_ACK_PENDING;
} else {
APPL_TRACE_WARNING("%s: remote a2dp started, cancel \
remote start timer", __func__);
btif_a2dp_source_cancel_remote_start();
if (reset_remote_start) {
int index = btif_av_get_tws_pair_idx(remote_start_idx);
if (index < btif_max_av_clients &&
btif_av_is_remote_started_set(index)) {
APPL_TRACE_WARNING("%s:Clear remote start for tws pair index",__func__);
btif_av_clear_remote_start_timer(index);
}
}
btif_dispatch_sm_event(
BTIF_AV_RESET_REMOTE_STARTED_FLAG_UPDATE_AUDIO_STATE_EVT,
&remote_start_idx, sizeof(remote_start_idx));
status = A2DP_CTRL_ACK_PENDING;
}
}
if (audio_start_awaited) {
if (reconfig_a2dp || (btif_av_is_under_handoff())) {
APPL_TRACE_DEBUG("Audio start awaited handle start under handoff");
audio_start_awaited = false;
btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
status = A2DP_CTRL_ACK_PENDING;
if (btif_av_get_peer_sep() == AVDT_TSEP_SRC)
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
}
/* In dual a2dp mode check for stream started first*/
if (btif_av_stream_started_ready()) {
/*
* Already started, setup audio data channel listener and ACK
* back immediately.
*/
APPL_TRACE_DEBUG("Av stream already started");
if (btif_a2dp_src_vsc.tx_start_initiated == TRUE) {
APPL_TRACE_DEBUG("VSC exchange alreday started on Handoff Start,wait");
status = A2DP_CTRL_ACK_PENDING;
break;
} else if (btif_a2dp_src_vsc.tx_started == FALSE) {
uint8_t hdl = 0;
APPL_TRACE_DEBUG("%s: latest playing idx = %d",__func__,
latest_playing_idx);
if (latest_playing_idx >= btif_max_av_clients ||
latest_playing_idx < 0) {
APPL_TRACE_ERROR("%s: Invalid index",__func__);
status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;//status to stop start retry
break;
}
if (remote_start_flag) {
if (btif_av_is_split_a2dp_enabled()) {
hdl = btif_av_get_av_hdl_from_idx(latest_playing_idx);
APPL_TRACE_DEBUG("Start VSC exchange on MM Start when state \
is remote started on hdl = %d",hdl);
btif_dispatch_sm_event(BTIF_AV_OFFLOAD_START_REQ_EVT, (char *)&hdl, 1);
status = A2DP_CTRL_ACK_PENDING;
} else {
APPL_TRACE_DEBUG("Av stream already remote started in NS mode");
start_audio = true;
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
} else if (btif_av_is_state_opened(latest_playing_idx)) {
btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
status = A2DP_CTRL_ACK_SUCCESS;
} else {
/*Return pending and ack when start stream cfm received from remote*/
status = A2DP_CTRL_ACK_PENDING;
}
} else if (reconfig_a2dp) {
APPL_TRACE_DEBUG("%s: wait for reconfig to complete ",__func__);
status = A2DP_CTRL_ACK_LONG_WAIT_ERR;
} else {
APPL_TRACE_DEBUG("%s: respond with success as already started",__func__);
if (!btif_av_is_split_a2dp_enabled() && !btif_a2dp_source_is_streaming())
start_audio = true;
status = A2DP_CTRL_ACK_SUCCESS;
}
#if (TWS_ENABLED == TRUE)
if (btif_av_current_device_is_tws() &&
reset_remote_start &&
!btif_av_is_tws_device_playing(latest_playing_idx)) {
int pair_idx = btif_av_get_tws_pair_idx(latest_playing_idx);
if (pair_idx < btif_max_av_clients &&
btif_av_is_state_opened(pair_idx)) {
APPL_TRACE_DEBUG("%s:Other TWS+ is not start at idx %d, \
sending start_req",__func__,pair_idx);
btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
}
status = A2DP_CTRL_ACK_PENDING;
} else if (remote_start_idx < btif_max_av_clients &&
reset_remote_start && btif_av_current_device_is_tws()) {
uint8_t hdl = 0;
int pair_index = remote_start_idx;
if (remote_start_flag) {
//Both the earbuds are are remote started, fetch index pair to send offload req
pair_index = btif_av_get_tws_pair_idx(latest_playing_idx);
}
hdl = btif_av_get_av_hdl_from_idx(pair_index);
APPL_TRACE_DEBUG("Start VSC exchange for remote started index of \
TWS+ device");
btif_dispatch_sm_event(BTIF_AV_OFFLOAD_START_REQ_EVT, (char *)&hdl, 1);
status = A2DP_CTRL_ACK_PENDING;
}
#endif
break;
}
if (btif_av_current_device_is_tws()) {
int index = btif_av_get_latest_stream_device_idx();
btif_dispatch_sm_event(BTIF_AV_REPORT_AUDIO_STATE_EVT,
(char *)&index, 1);
}
btif_av_reset_reconfig_flag();
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
if (btif_av_stream_ready()) {
/*
* Post start event and wait for audio path to open.
* If we are the source, the ACK will be sent after the start
* procedure is completed, othewise send it now.
*/
if (latest_playing_idx < btif_max_av_clients &&
btif_av_is_state_opened(latest_playing_idx)) {
btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
/*Return pending and ack when start stream cfm received from remote*/
status = A2DP_CTRL_ACK_PENDING;
break;
}
}
APPL_TRACE_WARNING("%s: A2DP command %s while AV stream is not ready",
__func__, audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd));
if (latest_playing_idx >= btif_max_av_clients || latest_playing_idx < 0) {
status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;//status to stop start retry
} else {
status = A2DP_CTRL_ACK_FAILURE;
}
break;
}
case A2DP_CTRL_CMD_STOP: {
if (!bta_sys_is_register(BTA_ID_AV)) {
APPL_TRACE_ERROR("AV is disabled, return disc in progress");
status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;
break;
}
if (btif_ba_is_active())
{
ba_send_message(BTIF_BA_AUDIO_STOP_REQ_EVT, 0, NULL, false);
status = A2DP_CTRL_ACK_PENDING;
break;
}
if ((!btif_av_is_split_a2dp_enabled() && btif_av_get_peer_sep()
== AVDT_TSEP_SNK &&
!btif_a2dp_source_is_streaming()) ||
(btif_av_is_split_a2dp_enabled() && btif_av_get_peer_sep()
== AVDT_TSEP_SNK &&
btif_a2dp_src_vsc.tx_started == FALSE)) {
/* We are already stopped, just ack back */
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
btif_dispatch_sm_event(BTIF_AV_STOP_STREAM_REQ_EVT, NULL, 0);
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
case A2DP_CTRL_CMD_SUSPEND: {
int curr_idx = btif_av_get_latest_device_idx_to_start();
if (!bta_sys_is_register(BTA_ID_AV)) {
APPL_TRACE_ERROR("AV is disabled, return disc in progress");
status = A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS;
break;
}
/* Local suspend */
if (btif_ba_is_active()) {
ba_send_message(BTIF_BA_AUDIO_PAUSE_REQ_EVT, 0, NULL, false);
status = A2DP_CTRL_ACK_PENDING;
break;
}
if (reconfig_a2dp ||
((btif_a2dp_source_last_remote_start_index() ==
btif_av_get_latest_device_idx_to_start()) &&
(btif_av_is_remote_started_set(
btif_a2dp_source_last_remote_start_index())))) {
LOG(WARNING) << __func__ << ": Suspend called due to reconfig";
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
if (btif_av_stream_started_ready()) {
APPL_TRACE_DEBUG("Suspend stream request to Av");
btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
status = A2DP_CTRL_ACK_PENDING;
break;
}else if (btif_av_current_device_is_tws()) {
//Check if either of the index is streaming
for (int i = 0; i < btif_max_av_clients; i++) {
if (btif_av_is_tws_device_playing(i) &&
!btif_av_is_tws_suspend_triggered(i)) {
APPL_TRACE_DEBUG("Suspend TWS+ stream on index %d",i);
btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
status = A2DP_CTRL_ACK_PENDING;
break;
}
}
if (status == A2DP_CTRL_ACK_PENDING) {
btif_av_clear_remote_suspend_flag();
break;
}
}
/*Need to check if start is triggered and it is not in started state*/
btif_av_clear_pending_start_flag();
/*pls check if we need to add a condition here */
/* If we are not in started state, just ack back ok and let
* audioflinger close the channel. This can happen if we are
* remotely suspended, clear REMOTE SUSPEND flag.
*/
if (btif_av_check_flag_remote_suspend(curr_idx) && btif_a2dp_source_is_streaming()) {
APPL_TRACE_DEBUG("Suspend called when active dev remote suspended and media alarm active, stop timer");
btif_a2dp_source_stop_audio_req();
}
btif_av_clear_remote_suspend_flag();
status = A2DP_CTRL_ACK_SUCCESS;
break;
}
default:
APPL_TRACE_ERROR("UNSUPPORTED CMD (%d)", cmd);
status = A2DP_CTRL_ACK_FAILURE;
break;
}
// send the response now based on status
switch (cmd) {
case A2DP_CTRL_CMD_START:
#if AHIM_ENABLED
btif_ahim_ack_stream_started(status, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_started(status);
#endif
if (start_audio) {
btif_a2dp_source_start_audio_req();
}
break;
case A2DP_CTRL_CMD_SUSPEND:
case A2DP_CTRL_CMD_STOP:
#if AHIM_ENABLED
btif_ahim_ack_stream_suspended(status, A2DP);
#else
bluetooth::audio::a2dp::ack_stream_suspended(status);
#endif
break;
default:
break;
}
}
thread_t* get_worker_thread() {
return btif_a2dp_source_cb.worker_thread;
}