| /****************************************************************************** |
| * |
| * Copyright 2004-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. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * This file contains functions for managing the SCO connection used in AG. |
| * |
| ******************************************************************************/ |
| |
| #include <android_bluetooth_flags.h> |
| #include <base/functional/bind.h> |
| #include <base/logging.h> |
| #include <bluetooth/log.h> |
| |
| #include <cstdint> |
| |
| #include "audio_hal_interface/hfp_client_interface.h" |
| #include "bta/ag/bta_ag_int.h" |
| #include "bta_ag_swb_aptx.h" |
| #include "common/init_flags.h" |
| #include "device/include/controller.h" |
| #include "internal_include/bt_target.h" |
| #include "internal_include/bt_trace.h" |
| #include "os/log.h" |
| #include "osi/include/osi.h" // UNUSED_ATTR |
| #include "stack/btm/btm_int_types.h" |
| #include "stack/btm/btm_sco.h" |
| #include "stack/btm/btm_sco_hfp_hal.h" |
| #include "stack/include/btm_api.h" |
| #include "stack/include/main_thread.h" |
| #include "types/raw_address.h" |
| |
| extern tBTM_CB btm_cb; |
| |
| using HfpInterface = bluetooth::audio::hfp::HfpClientInterface; |
| using namespace bluetooth; |
| |
| /* Codec negotiation timeout */ |
| #ifndef BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS |
| #define BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS (3 * 1000) /* 3 seconds */ |
| #endif |
| |
| #define BTM_VOICE_SETTING_CVSD \ |
| ((uint16_t)(HCI_INP_CODING_LINEAR | HCI_INP_DATA_FMT_2S_COMPLEMENT | \ |
| HCI_INP_SAMPLE_SIZE_16BIT | HCI_AIR_CODING_FORMAT_CVSD)) |
| |
| #define BTM_VOICE_SETTING_TRANS \ |
| ((uint16_t)(HCI_INP_CODING_LINEAR | HCI_INP_DATA_FMT_2S_COMPLEMENT | \ |
| HCI_INP_SAMPLE_SIZE_16BIT | HCI_AIR_CODING_FORMAT_TRANSPNT)) |
| |
| static bool sco_allowed = true; |
| static RawAddress active_device_addr = {}; |
| static std::unique_ptr<HfpInterface> hfp_client_interface; |
| static std::unique_ptr<HfpInterface::Offload> hfp_offload_interface; |
| static std::unordered_map<int, ::hfp::sco_config> sco_config_map; |
| static std::unordered_map<tBTA_AG_PEER_CODEC, esco_coding_format_t> |
| codec_coding_format_map{ |
| {UUID_CODEC_LC3, ESCO_CODING_FORMAT_LC3}, |
| {UUID_CODEC_MSBC, ESCO_CODING_FORMAT_MSBC}, |
| {UUID_CODEC_CVSD, ESCO_CODING_FORMAT_CVSD}, |
| }; |
| |
| /* sco events */ |
| enum { |
| BTA_AG_SCO_LISTEN_E, /* listen request */ |
| BTA_AG_SCO_OPEN_E, /* open request */ |
| BTA_AG_SCO_XFER_E, /* transfer request */ |
| BTA_AG_SCO_CN_DONE_E, /* codec negotiation done */ |
| BTA_AG_SCO_REOPEN_E, /* Retry with other codec when failed */ |
| BTA_AG_SCO_CLOSE_E, /* close request */ |
| BTA_AG_SCO_SHUTDOWN_E, /* shutdown request */ |
| BTA_AG_SCO_CONN_OPEN_E, /* sco open */ |
| BTA_AG_SCO_CONN_CLOSE_E, /* sco closed */ |
| }; |
| |
| #define CASE_RETURN_STR(const) \ |
| case const: \ |
| return #const; |
| |
| static const char* bta_ag_sco_evt_str(uint8_t event) { |
| switch (event) { |
| CASE_RETURN_STR(BTA_AG_SCO_LISTEN_E) |
| CASE_RETURN_STR(BTA_AG_SCO_OPEN_E) |
| CASE_RETURN_STR(BTA_AG_SCO_XFER_E) |
| CASE_RETURN_STR(BTA_AG_SCO_CN_DONE_E) |
| CASE_RETURN_STR(BTA_AG_SCO_REOPEN_E) |
| CASE_RETURN_STR(BTA_AG_SCO_CLOSE_E) |
| CASE_RETURN_STR(BTA_AG_SCO_SHUTDOWN_E) |
| CASE_RETURN_STR(BTA_AG_SCO_CONN_OPEN_E) |
| CASE_RETURN_STR(BTA_AG_SCO_CONN_CLOSE_E) |
| default: |
| return "Unknown SCO Event"; |
| } |
| } |
| |
| static const char* bta_ag_sco_state_str(uint8_t state) { |
| switch (state) { |
| CASE_RETURN_STR(BTA_AG_SCO_SHUTDOWN_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_LISTEN_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_CODEC_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_OPENING_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_OPEN_CL_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_OPEN_XFER_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_OPEN_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_CLOSING_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_CLOSE_OP_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_CLOSE_XFER_ST) |
| CASE_RETURN_STR(BTA_AG_SCO_SHUTTING_ST) |
| default: |
| return "Unknown SCO State"; |
| } |
| } |
| |
| /** |
| * Check if bd_addr is the current active device. |
| * |
| * @param bd_addr target device address |
| * @return True if bd_addr is the current active device, False otherwise or if |
| * no active device is set (i.e. active_device_addr is empty) |
| */ |
| bool bta_ag_sco_is_active_device(const RawAddress& bd_addr) { |
| return !active_device_addr.IsEmpty() && active_device_addr == bd_addr; |
| } |
| |
| void updateCodecParametersFromProviderInfo(tBTA_AG_PEER_CODEC esco_codec, |
| enh_esco_params_t& params); |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_conn_cback |
| * |
| * Description BTM SCO connection callback. |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void bta_ag_sco_conn_cback(uint16_t sco_idx) { |
| uint16_t handle; |
| tBTA_AG_SCB* p_scb; |
| |
| /* match callback to scb; first check current sco scb */ |
| if (bta_ag_cb.sco.p_curr_scb != nullptr && bta_ag_cb.sco.p_curr_scb->in_use) { |
| handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); |
| } |
| /* then check for scb connected to this peer */ |
| else { |
| /* Check if SLC is up */ |
| handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_idx)); |
| p_scb = bta_ag_scb_by_idx(handle); |
| if (p_scb && !p_scb->svc_conn) handle = 0; |
| } |
| |
| if (handle != 0) { |
| do_in_main_thread( |
| FROM_HERE, base::BindOnce(&bta_ag_sm_execute_by_handle, handle, |
| BTA_AG_SCO_OPEN_EVT, tBTA_AG_DATA::kEmpty)); |
| } else { |
| /* no match found; disconnect sco, init sco variables */ |
| bta_ag_cb.sco.p_curr_scb = nullptr; |
| bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; |
| BTM_RemoveSco(sco_idx); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_disc_cback |
| * |
| * Description BTM SCO disconnection callback. |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void bta_ag_sco_disc_cback(uint16_t sco_idx) { |
| uint16_t handle = 0; |
| |
| log::debug("sco_idx: 0x{:x} sco.state:{}", sco_idx, |
| sco_state_text(static_cast<tSCO_STATE>(bta_ag_cb.sco.state))); |
| log::debug("scb[0] in_use:{} sco_idx: 0x{:x} sco state:{}", |
| logbool(bta_ag_cb.scb[0].in_use), bta_ag_cb.scb[0].sco_idx, |
| sco_state_text(static_cast<tSCO_STATE>(bta_ag_cb.scb[0].state))); |
| log::debug("scb[1] in_use:{} sco_idx:0x{:x} sco state:{}", |
| logbool(bta_ag_cb.scb[1].in_use), bta_ag_cb.scb[1].sco_idx, |
| sco_state_text(static_cast<tSCO_STATE>(bta_ag_cb.scb[1].state))); |
| |
| /* match callback to scb */ |
| if (bta_ag_cb.sco.p_curr_scb != nullptr && bta_ag_cb.sco.p_curr_scb->in_use) { |
| /* We only care about callbacks for the active SCO */ |
| if (bta_ag_cb.sco.p_curr_scb->sco_idx != sco_idx) { |
| if (bta_ag_cb.sco.p_curr_scb->sco_idx != 0xFFFF) return; |
| } |
| handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb); |
| } |
| |
| if (handle != 0) { |
| const bool aptx_voice = |
| is_hfp_aptx_voice_enabled() && |
| (bta_ag_cb.sco.p_curr_scb->is_aptx_swb_codec == true) && |
| (bta_ag_cb.sco.p_curr_scb->inuse_codec == |
| BTA_AG_SCO_APTX_SWB_SETTINGS_Q0); |
| log::verbose("aptx_voice={}, inuse_codec={:#x}", logbool(aptx_voice), |
| bta_ag_cb.sco.p_curr_scb->inuse_codec); |
| |
| /* Restore settings */ |
| if (bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_MSBC || |
| bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_LC3 || aptx_voice) { |
| /* Bypass vendor specific and voice settings if enhanced eSCO supported */ |
| if (!(controller_get_interface() |
| ->supports_enhanced_setup_synchronous_connection())) { |
| BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); |
| } |
| |
| /* If SCO open was initiated by AG and failed for mSBC T2, try mSBC T1 |
| * 'Safe setting' first. If T1 also fails, try CVSD |
| * same operations for LC3 settings */ |
| if (bta_ag_sco_is_opening(bta_ag_cb.sco.p_curr_scb)) { |
| bta_ag_cb.sco.p_curr_scb->state = BTA_AG_SCO_CODEC_ST; |
| if (bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_LC3) { |
| if (bta_ag_cb.sco.p_curr_scb->codec_lc3_settings == |
| BTA_AG_SCO_LC3_SETTINGS_T2) { |
| log::warn( |
| "eSCO/SCO failed to open, falling back to LC3 T1 settings"); |
| bta_ag_cb.sco.p_curr_scb->codec_lc3_settings = |
| BTA_AG_SCO_LC3_SETTINGS_T1; |
| } else { |
| log::warn("eSCO/SCO failed to open, falling back to CVSD settings"); |
| bta_ag_cb.sco.p_curr_scb->inuse_codec = UUID_CODEC_CVSD; |
| bta_ag_cb.sco.p_curr_scb->codec_fallback = true; |
| } |
| } else { |
| if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == |
| BTA_AG_SCO_MSBC_SETTINGS_T2) { |
| log::warn( |
| "eSCO/SCO failed to open, falling back to mSBC T1 settings"); |
| bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = |
| BTA_AG_SCO_MSBC_SETTINGS_T1; |
| |
| } else { |
| log::warn("eSCO/SCO failed to open, falling back to CVSD"); |
| bta_ag_cb.sco.p_curr_scb->inuse_codec = UUID_CODEC_CVSD; |
| bta_ag_cb.sco.p_curr_scb->codec_fallback = true; |
| } |
| } |
| } |
| } else if (bta_ag_sco_is_opening(bta_ag_cb.sco.p_curr_scb)) { |
| if (IS_FLAG_ENABLED(retry_esco_with_zero_retransmission_effort) && |
| bta_ag_cb.sco.p_curr_scb->retransmission_effort_retries == 0) { |
| bta_ag_cb.sco.p_curr_scb->retransmission_effort_retries++; |
| bta_ag_cb.sco.p_curr_scb->state = BTA_AG_SCO_CODEC_ST; |
| log::warn("eSCO/SCO failed to open, retry with retransmission_effort"); |
| } else { |
| log::error("eSCO/SCO failed to open, no more fall back"); |
| if (IS_FLAG_ENABLED(is_sco_managed_by_audio)) { |
| hfp_offload_interface->CancelStreamingRequest(); |
| } |
| } |
| } |
| |
| bta_ag_cb.sco.p_curr_scb->inuse_codec = BTM_SCO_CODEC_NONE; |
| |
| do_in_main_thread( |
| FROM_HERE, base::BindOnce(&bta_ag_sm_execute_by_handle, handle, |
| BTA_AG_SCO_CLOSE_EVT, tBTA_AG_DATA::kEmpty)); |
| } else { |
| /* no match found */ |
| log::verbose("no scb for ag_sco_disc_cback"); |
| |
| /* sco could be closed after scb dealloc'ed */ |
| if (bta_ag_cb.sco.p_curr_scb != nullptr) { |
| bta_ag_cb.sco.p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
| bta_ag_cb.sco.p_curr_scb = nullptr; |
| bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_remove_sco |
| * |
| * Description Removes the specified SCO from the system. |
| * If only_active is true, then SCO is only removed if |
| * connected |
| * |
| * Returns bool - true if SCO removal was started |
| * |
| ******************************************************************************/ |
| static bool bta_ag_remove_sco(tBTA_AG_SCB* p_scb, bool only_active) { |
| if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) { |
| if (!only_active || p_scb->sco_idx == bta_ag_cb.sco.cur_idx) { |
| tBTM_STATUS status = BTM_RemoveSco(p_scb->sco_idx); |
| log::debug("Removed SCO index:0x{:04x} status:{}", p_scb->sco_idx, |
| btm_status_text(status)); |
| if (status == BTM_CMD_STARTED) { |
| /* SCO is connected; set current control block */ |
| bta_ag_cb.sco.p_curr_scb = p_scb; |
| return true; |
| } else if ((status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR)) { |
| /* If no connection reset the SCO handle */ |
| p_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_esco_connreq_cback |
| * |
| * Description BTM eSCO connection requests and eSCO change requests |
| * Only the connection requests are processed by BTA. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, |
| tBTM_ESCO_EVT_DATA* p_data) { |
| /* Only process connection requests */ |
| if (event == BTM_ESCO_CONN_REQ_EVT) { |
| uint16_t sco_inx = p_data->conn_evt.sco_inx; |
| const RawAddress* remote_bda = BTM_ReadScoBdAddr(sco_inx); |
| tBTA_AG_SCB* p_scb = bta_ag_scb_by_idx(bta_ag_idx_by_bdaddr(remote_bda)); |
| if (remote_bda && bta_ag_sco_is_active_device(*remote_bda) && p_scb && |
| p_scb->svc_conn) { |
| p_scb->sco_idx = sco_inx; |
| |
| /* If no other SCO active, allow this one */ |
| if (!bta_ag_cb.sco.p_curr_scb) { |
| log::verbose("Accept Conn Request (sco_inx 0x{:04x})", sco_inx); |
| bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); |
| |
| bta_ag_cb.sco.state = BTA_AG_SCO_OPENING_ST; |
| bta_ag_cb.sco.p_curr_scb = p_scb; |
| bta_ag_cb.sco.cur_idx = p_scb->sco_idx; |
| } else { |
| /* Begin a transfer: Close current SCO before responding */ |
| log::verbose("bta_ag_esco_connreq_cback: Begin XFER"); |
| bta_ag_cb.sco.p_xfer_scb = p_scb; |
| bta_ag_cb.sco.conn_data = p_data->conn_evt; |
| bta_ag_cb.sco.state = BTA_AG_SCO_OPEN_XFER_ST; |
| |
| if (!bta_ag_remove_sco(bta_ag_cb.sco.p_curr_scb, true)) { |
| log::error( |
| "Nothing to remove,so accept Conn Request(sco_inx 0x{:04x})", |
| sco_inx); |
| bta_ag_cb.sco.p_xfer_scb = nullptr; |
| bta_ag_cb.sco.state = BTA_AG_SCO_LISTEN_ST; |
| |
| bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt); |
| } |
| } |
| } else { |
| log::warn( |
| "reject incoming SCO connection, remote_bda={}, active_bda={}, " |
| "current_bda={}", |
| ADDRESS_TO_LOGGABLE_STR(remote_bda ? *remote_bda |
| : RawAddress::kEmpty), |
| ADDRESS_TO_LOGGABLE_STR(active_device_addr), |
| ADDRESS_TO_LOGGABLE_STR(p_scb ? p_scb->peer_addr |
| : RawAddress::kEmpty)); |
| BTM_EScoConnRsp(p_data->conn_evt.sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, |
| (enh_esco_params_t*)nullptr); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_cback_sco |
| * |
| * Description Call application callback function with SCO event. |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void bta_ag_cback_sco(tBTA_AG_SCB* p_scb, tBTA_AG_EVT event) { |
| tBTA_AG_HDR sco = {}; |
| sco.handle = bta_ag_scb_to_idx(p_scb); |
| sco.app_id = p_scb->app_id; |
| /* call close cback */ |
| (*bta_ag_cb.p_cback)(static_cast<tBTA_AG_EVT>(event), (tBTA_AG*)&sco); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_create_sco |
| * |
| * Description Create a SCO connection for a given control block |
| * p_scb : Pointer to the target AG control block |
| * is_orig : Whether to initiate or listen for SCO connection |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_create_sco(tBTA_AG_SCB* p_scb, bool is_orig) { |
| log::debug("BEFORE {}", p_scb->ToString()); |
| tBTA_AG_PEER_CODEC esco_codec = UUID_CODEC_CVSD; |
| |
| if (!bta_ag_sco_is_active_device(p_scb->peer_addr)) { |
| log::warn("device {} is not active, active_device={}", |
| ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr), |
| ADDRESS_TO_LOGGABLE_STR(active_device_addr)); |
| if (bta_ag_cb.sco.p_curr_scb != nullptr && |
| bta_ag_cb.sco.p_curr_scb->in_use && p_scb == bta_ag_cb.sco.p_curr_scb) { |
| do_in_main_thread(FROM_HERE, base::BindOnce(&bta_ag_sm_execute, p_scb, |
| BTA_AG_SCO_CLOSE_EVT, |
| tBTA_AG_DATA::kEmpty)); |
| } |
| return; |
| } |
| /* Make sure this SCO handle is not already in use */ |
| if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) { |
| log::error("device {}, index 0x{:04x} already in use!", |
| ADDRESS_TO_LOGGABLE_CSTR(p_scb->peer_addr), p_scb->sco_idx); |
| return; |
| } |
| |
| #if (DISABLE_WBS == FALSE) |
| if ((p_scb->sco_codec == BTM_SCO_CODEC_MSBC) && !p_scb->codec_fallback && |
| hfp_hal_interface::get_wbs_supported()) { |
| esco_codec = UUID_CODEC_MSBC; |
| } |
| #endif |
| |
| if (is_hfp_aptx_voice_enabled()) { |
| if ((p_scb->sco_codec == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) && |
| !p_scb->codec_fallback) { |
| esco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; |
| } |
| } |
| |
| if ((p_scb->sco_codec == BTM_SCO_CODEC_LC3) && !p_scb->codec_fallback && |
| hfp_hal_interface::get_swb_supported()) { |
| esco_codec = UUID_CODEC_LC3; |
| } |
| |
| if (p_scb->codec_fallback) { |
| p_scb->codec_fallback = false; |
| /* Force AG to send +BCS for the next audio connection. */ |
| p_scb->codec_updated = true; |
| /* Reset mSBC settings to T2 for the next audio connection */ |
| p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
| /* Reset LC3 settings to T2 for the next audio connection */ |
| p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; |
| /* Reset SWB settings to Q3 for the next audio connection */ |
| p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; |
| } |
| |
| bool offload = hfp_hal_interface::get_offload_enabled(); |
| /* Initialize eSCO parameters */ |
| enh_esco_params_t params = {}; |
| /* If SWB/WBS are excluded, use CVSD by default, |
| * index is 0 for CVSD by initialization. |
| * If eSCO codec is mSBC, index is T2 or T1. |
| * If eSCO coedc is LC3, index is T2 or T1. */ |
| log::warn("esco_codec: {}", (int)esco_codec); |
| if (esco_codec == UUID_CODEC_LC3) { |
| if (p_scb->codec_lc3_settings == BTA_AG_SCO_LC3_SETTINGS_T2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_LC3_T2, offload); |
| } else { |
| params = esco_parameters_for_codec(ESCO_CODEC_LC3_T1, offload); |
| } |
| } else if (esco_codec == UUID_CODEC_MSBC) { |
| if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T2, offload); |
| } else { |
| params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1, offload); |
| } |
| } else if (is_hfp_aptx_voice_enabled() && |
| (p_scb->is_aptx_swb_codec == true && !p_scb->codec_updated)) { |
| if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q3) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q3, true); |
| } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q2, true); |
| } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q1) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q1, true); |
| } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q0, true); |
| } |
| } else { |
| if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && |
| (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) { |
| // HFP >=1.7 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S4, offload); |
| } else { |
| // HFP <=1.6 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S3, offload); |
| } |
| } |
| |
| updateCodecParametersFromProviderInfo(esco_codec, params); |
| |
| if (IS_FLAG_ENABLED(retry_esco_with_zero_retransmission_effort) && |
| p_scb->retransmission_effort_retries == 1) { |
| log::info("change retransmission_effort to 0, retry"); |
| p_scb->retransmission_effort_retries++; |
| params.retransmission_effort = ESCO_RETRANSMISSION_OFF; |
| } |
| |
| /* Configure input/output data path based on HAL settings. */ |
| hfp_hal_interface::set_codec_datapath(esco_codec); |
| hfp_hal_interface::update_esco_parameters(¶ms); |
| |
| /* If initiating, setup parameters to start SCO/eSCO connection */ |
| if (is_orig) { |
| bta_ag_cb.sco.is_local = true; |
| /* Set eSCO Mode */ |
| BTM_SetEScoMode(¶ms); |
| bta_ag_cb.sco.p_curr_scb = p_scb; |
| /* save the current codec as sco_codec can be updated while SCO is open. */ |
| p_scb->inuse_codec = esco_codec; |
| |
| /* tell sys to stop av if any */ |
| bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| |
| /* Send pending commands to create SCO connection to peer */ |
| bta_ag_create_pending_sco(p_scb, bta_ag_cb.sco.is_local); |
| log::debug("Initiating AG SCO inx 0x{:04x}, pkt types 0x{:04x}", |
| p_scb->sco_idx, params.packet_types); |
| } else { |
| /* Not initiating, go to listen mode */ |
| tBTM_STATUS btm_status = BTM_CreateSco( |
| &p_scb->peer_addr, false, params.packet_types, &p_scb->sco_idx, |
| bta_ag_sco_conn_cback, bta_ag_sco_disc_cback); |
| if (btm_status == BTM_CMD_STARTED) { |
| BTM_RegForEScoEvts(p_scb->sco_idx, bta_ag_esco_connreq_cback); |
| } |
| log::debug("Listening AG SCO inx 0x{:04x} status:{} pkt types 0x{:04x}", |
| p_scb->sco_idx, btm_status_text(btm_status), |
| params.packet_types); |
| } |
| log::debug("AFTER {}", p_scb->ToString()); |
| } |
| |
| void updateCodecParametersFromProviderInfo(tBTA_AG_PEER_CODEC esco_codec, |
| enh_esco_params_t& params) { |
| if (IS_FLAG_ENABLED(is_sco_managed_by_audio) && !sco_config_map.empty()) { |
| auto sco_config_it = sco_config_map.find(esco_codec); |
| if (sco_config_it == sco_config_map.end()) { |
| log::error("cannot find sco config for esco_codec index={}", esco_codec); |
| return; |
| } |
| log::debug("use ProviderInfo to update (e)sco parameters"); |
| params.input_data_path = sco_config_it->second.inputDataPath; |
| params.output_data_path = sco_config_it->second.outputDataPath; |
| if (!sco_config_it->second.useControllerCodec) { |
| log::debug("use DSP Codec instead of controller codec"); |
| |
| esco_coding_format_t codingFormat = codec_coding_format_map[esco_codec]; |
| params.input_coding_format.coding_format = codingFormat; |
| params.output_coding_format.coding_format = codingFormat; |
| params.input_bandwidth = TXRX_64KBITS_RATE; |
| params.output_bandwidth = TXRX_64KBITS_RATE; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_create_pending_sco |
| * |
| * Description This Function is called after the pre-SCO vendor setup is |
| * done for the BTA to continue and send the HCI Commands for |
| * creating/accepting SCO connection with peer based on the |
| * is_local parameter. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_create_pending_sco(tBTA_AG_SCB* p_scb, bool is_local) { |
| tBTA_AG_PEER_CODEC esco_codec = p_scb->inuse_codec; |
| enh_esco_params_t params = {}; |
| bool offload = hfp_hal_interface::get_offload_enabled(); |
| bta_ag_cb.sco.p_curr_scb = p_scb; |
| bta_ag_cb.sco.cur_idx = p_scb->sco_idx; |
| |
| /* Local device requested SCO connection to peer */ |
| if (is_local) { |
| if (esco_codec == UUID_CODEC_LC3) { |
| if (p_scb->codec_lc3_settings == BTA_AG_SCO_LC3_SETTINGS_T2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_LC3_T2, offload); |
| } else { |
| params = esco_parameters_for_codec(ESCO_CODEC_LC3_T1, offload); |
| } |
| } else if (is_hfp_aptx_voice_enabled() && |
| (p_scb->is_aptx_swb_codec == true && !p_scb->codec_updated)) { |
| if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q3) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q3, true); |
| } else if (p_scb->codec_aptx_settings == |
| BTA_AG_SCO_APTX_SWB_SETTINGS_Q2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q2, true); |
| } else if (p_scb->codec_aptx_settings == |
| BTA_AG_SCO_APTX_SWB_SETTINGS_Q1) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q1, true); |
| } else if (p_scb->codec_aptx_settings == |
| BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) { |
| params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q0, true); |
| } |
| } else if (esco_codec == UUID_CODEC_MSBC) { |
| if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { |
| params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T2, offload); |
| } else { |
| params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1, offload); |
| } |
| } else { |
| if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && |
| (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) { |
| // HFP >=1.7 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S4, offload); |
| } else { |
| // HFP <=1.6 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S3, offload); |
| } |
| } |
| |
| /* Bypass voice settings if enhanced SCO setup command is supported */ |
| if (!(controller_get_interface() |
| ->supports_enhanced_setup_synchronous_connection())) { |
| if (esco_codec == UUID_CODEC_MSBC || esco_codec == UUID_CODEC_LC3) { |
| BTM_WriteVoiceSettings(BTM_VOICE_SETTING_TRANS); |
| } else { |
| BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); |
| } |
| } |
| |
| if (BTM_CreateSco(&p_scb->peer_addr, true, params.packet_types, |
| &p_scb->sco_idx, bta_ag_sco_conn_cback, |
| bta_ag_sco_disc_cback) == BTM_CMD_STARTED) { |
| /* Initiating the connection, set the current sco handle */ |
| bta_ag_cb.sco.cur_idx = p_scb->sco_idx; |
| /* Configure input/output data. */ |
| hfp_hal_interface::set_codec_datapath(esco_codec); |
| } |
| log::verbose("initiated SCO connection"); |
| } else { |
| // Local device accepted SCO connection from peer(HF) |
| // Because HF devices usually do not send AT+BAC and +BCS command, |
| // and there is no plan to implement corresponding command handlers, |
| // so we only accept CVSD connection from HF no matter what's |
| // requested. |
| if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && |
| (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) { |
| // HFP >=1.7 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S4, offload); |
| } else { |
| // HFP <=1.6 eSCO |
| params = esco_parameters_for_codec(ESCO_CODEC_CVSD_S3, offload); |
| } |
| |
| // HFP v1.8 5.7.3 CVSD coding |
| tSCO_CONN* p_sco = NULL; |
| if (p_scb->sco_idx < BTM_MAX_SCO_LINKS) |
| p_sco = &btm_cb.sco_cb.sco_db[p_scb->sco_idx]; |
| if (p_sco && (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO || |
| !btm_peer_supports_esco_ev3(p_sco->esco.data.bd_addr))) { |
| params = esco_parameters_for_codec(SCO_CODEC_CVSD_D1, offload); |
| } |
| |
| BTM_EScoConnRsp(p_scb->sco_idx, HCI_SUCCESS, ¶ms); |
| log::verbose("listening for SCO connection"); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_codec_negotiation_timer_cback |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void bta_ag_codec_negotiation_timer_cback(void* data) { |
| log::warn("Codec negotiation timeout"); |
| tBTA_AG_SCB* p_scb = (tBTA_AG_SCB*)data; |
| |
| /* Announce that codec negotiation failed. */ |
| bta_ag_sco_codec_nego(p_scb, false); |
| |
| /* call app callback */ |
| bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_codec_negotiate |
| * |
| * Description Initiate codec negotiation by sending AT command. |
| * If not necessary, skip negotiation. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_codec_negotiate(tBTA_AG_SCB* p_scb) { |
| bta_ag_cb.sco.p_curr_scb = p_scb; |
| uint8_t* p_rem_feat = BTM_ReadRemoteFeatures(p_scb->peer_addr); |
| bool sdp_wbs_support = p_scb->peer_sdp_features & BTA_AG_FEAT_WBS_SUPPORT; |
| |
| if (p_rem_feat == nullptr) { |
| log::warn("Skip codec negotiation, failed to read remote features"); |
| bta_ag_sco_codec_nego(p_scb, false); |
| return; |
| } |
| |
| // Workaround for misbehaving HFs, which indicate which one is not support on |
| // Transparent Synchronous Data in Remote Supported Features, WBS in SDP and |
| // and Codec Negotiation in BRSF. Fluoride will assume CVSD codec by default. |
| // In Sony XAV AX100 car kit and Sony MW600 Headset case, which indicate |
| // Transparent Synchronous Data and WBS support, but no codec negotiation |
| // support, using mSBC codec can result background noise or no audio. |
| // In Skullcandy JIB case, which indicate WBS and codec negotiation support, |
| // but no Transparent Synchronous Data support, using mSBC codec can result |
| // SCO setup fail by Firmware reject. |
| if (!HCI_LMP_TRANSPNT_SUPPORTED(p_rem_feat) || !sdp_wbs_support || |
| !(p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) { |
| log::info("Assume CVSD by default due to mask mismatch"); |
| p_scb->sco_codec = UUID_CODEC_CVSD; |
| } |
| const bool aptx_voice = |
| is_hfp_aptx_voice_enabled() && p_scb->is_aptx_swb_codec && |
| (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK); |
| log::verbose( |
| "aptx_voice={}, is_aptx_swb_codec={}, Q0 codec supported={}", |
| logbool(aptx_voice), logbool(p_scb->is_aptx_swb_codec), |
| logbool(p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK)); |
| |
| if (((p_scb->codec_updated || p_scb->codec_fallback) && |
| (p_scb->features & BTA_AG_FEAT_CODEC) && |
| (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) || |
| (aptx_voice)) { |
| log::info("Starting codec negotiation"); |
| /* Change the power mode to Active until SCO open is completed. */ |
| bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| |
| if (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK) { |
| if (p_scb->is_aptx_swb_codec == false) { |
| p_scb->sco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; |
| p_scb->is_aptx_swb_codec = true; |
| } |
| log::verbose("Sending +QCS, sco_codec={}, is_aptx_swb_codec={}", |
| p_scb->sco_codec, logbool(p_scb->is_aptx_swb_codec)); |
| /* Send +QCS to the peer */ |
| bta_ag_send_qcs(p_scb, NULL); |
| } else { |
| if (aptx_voice) { |
| p_scb->sco_codec = BTM_SCO_CODEC_MSBC; |
| p_scb->is_aptx_swb_codec = false; |
| } |
| log::verbose("Sending +BCS, sco_codec={}, is_aptx_swb_codec={}", |
| p_scb->sco_codec, logbool(p_scb->is_aptx_swb_codec)); |
| /* Send +BCS to the peer */ |
| bta_ag_send_bcs(p_scb); |
| } |
| |
| /* Start timer to handle timeout */ |
| alarm_set_on_mloop(p_scb->codec_negotiation_timer, |
| BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS, |
| bta_ag_codec_negotiation_timer_cback, p_scb); |
| } else { |
| /* use same codec type as previous SCO connection, skip codec negotiation */ |
| log::info("Skip codec negotiation, using the same codec"); |
| bta_ag_sco_codec_nego(p_scb, true); |
| } |
| } |
| |
| static void bta_ag_sco_event(tBTA_AG_SCB* p_scb, uint8_t event) { |
| tBTA_AG_SCO_CB* p_sco = &bta_ag_cb.sco; |
| uint8_t previous_state = p_sco->state; |
| log::info("device:{} index:0x{:04x} state:{}[{}] event:{}[{}]", |
| ADDRESS_TO_LOGGABLE_CSTR(p_scb->peer_addr), p_scb->sco_idx, |
| bta_ag_sco_state_str(p_sco->state), p_sco->state, |
| bta_ag_sco_evt_str(event), event); |
| |
| switch (p_sco->state) { |
| case BTA_AG_SCO_SHUTDOWN_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_SHUTDOWN_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_LISTEN_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection (Additional channel) */ |
| bta_ag_create_sco(p_scb, false); |
| break; |
| |
| case BTA_AG_SCO_OPEN_E: |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| |
| #if (DISABLE_WBS == FALSE) |
| /* start codec negotiation */ |
| p_sco->state = BTA_AG_SCO_CODEC_ST; |
| bta_ag_codec_negotiate(p_scb); |
| #else |
| bta_ag_create_sco(p_scb, true); |
| p_sco->state = BTA_AG_SCO_OPENING_ST; |
| #endif |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| |
| if (p_scb == p_sco->p_curr_scb) p_sco->p_curr_scb = nullptr; |
| |
| /* If last SCO instance then finish shutting down */ |
| if (!bta_ag_other_scb_open(p_scb)) { |
| p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; |
| } |
| break; |
| |
| case BTA_AG_SCO_CLOSE_E: |
| /* remove listening connection */ |
| /* Ignore the event. Keep listening SCO for the active SLC */ |
| log::warn("BTA_AG_SCO_LISTEN_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* sco failed; create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_LISTEN_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_CODEC_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection (Additional channel) */ |
| bta_ag_create_sco(p_scb, false); |
| break; |
| |
| case BTA_AG_SCO_CN_DONE_E: |
| /* create sco connection to peer */ |
| bta_ag_create_sco(p_scb, true); |
| p_sco->state = BTA_AG_SCO_OPENING_ST; |
| break; |
| |
| case BTA_AG_SCO_XFER_E: |
| /* save xfer scb */ |
| p_sco->p_xfer_scb = p_scb; |
| p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| |
| if (p_scb == p_sco->p_curr_scb) p_sco->p_curr_scb = nullptr; |
| |
| /* If last SCO instance then finish shutting down */ |
| if (!bta_ag_other_scb_open(p_scb)) { |
| p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; |
| } |
| break; |
| |
| case BTA_AG_SCO_CLOSE_E: |
| if (bluetooth::common::init_flags:: |
| sco_codec_timeout_clear_is_enabled()) { |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| |
| if (p_scb == p_sco->p_curr_scb) p_sco->p_curr_scb = nullptr; |
| |
| bta_ag_create_sco(p_scb, false); |
| } |
| /* sco open is not started yet. just go back to listening */ |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* sco failed; create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_CODEC_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_OPENING_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* second headset has now joined */ |
| /* create sco listen connection (Additional channel) */ |
| if (p_scb != p_sco->p_curr_scb) { |
| bta_ag_create_sco(p_scb, false); |
| } |
| break; |
| |
| #if (DISABLE_WBS == FALSE) |
| case BTA_AG_SCO_REOPEN_E: |
| /* start codec negotiation */ |
| p_sco->state = BTA_AG_SCO_CODEC_ST; |
| bta_ag_codec_negotiate(p_scb); |
| break; |
| #endif |
| |
| case BTA_AG_SCO_XFER_E: |
| /* save xfer scb */ |
| p_sco->p_xfer_scb = p_scb; |
| p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; |
| break; |
| |
| case BTA_AG_SCO_CLOSE_E: |
| p_sco->state = BTA_AG_SCO_OPEN_CL_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* If not opening scb, just close it */ |
| if (p_scb != p_sco->p_curr_scb) { |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| } else |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| |
| break; |
| |
| case BTA_AG_SCO_CONN_OPEN_E: |
| p_sco->state = BTA_AG_SCO_OPEN_ST; |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* sco failed; create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_OPENING_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_OPEN_CL_ST: |
| switch (event) { |
| case BTA_AG_SCO_XFER_E: |
| /* save xfer scb */ |
| p_sco->p_xfer_scb = p_scb; |
| |
| p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; |
| break; |
| |
| case BTA_AG_SCO_OPEN_E: |
| p_sco->state = BTA_AG_SCO_OPENING_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* If not opening scb, just close it */ |
| if (p_scb != p_sco->p_curr_scb) { |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| } else |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| |
| break; |
| |
| case BTA_AG_SCO_CONN_OPEN_E: |
| /* close sco connection */ |
| bta_ag_remove_sco(p_scb, true); |
| |
| p_sco->state = BTA_AG_SCO_CLOSING_ST; |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* sco failed; create sco listen connection */ |
| |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_OPEN_CL_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_OPEN_XFER_ST: |
| switch (event) { |
| case BTA_AG_SCO_CLOSE_E: |
| /* close sco connection */ |
| bta_ag_remove_sco(p_scb, true); |
| |
| p_sco->state = BTA_AG_SCO_CLOSING_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* remove all connection */ |
| bta_ag_remove_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* closed sco; place in listen mode and |
| accept the transferred connection */ |
| bta_ag_create_sco(p_scb, false); /* Back into listen mode */ |
| |
| /* Accept sco connection with xfer scb */ |
| bta_ag_sco_conn_rsp(p_sco->p_xfer_scb, &p_sco->conn_data); |
| p_sco->state = BTA_AG_SCO_OPENING_ST; |
| p_sco->p_curr_scb = p_sco->p_xfer_scb; |
| p_sco->cur_idx = p_sco->p_xfer_scb->sco_idx; |
| p_sco->p_xfer_scb = nullptr; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_OPEN_XFER_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_OPEN_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* second headset has now joined */ |
| /* create sco listen connection (Additional channel) */ |
| if (p_scb != p_sco->p_curr_scb) { |
| bta_ag_create_sco(p_scb, false); |
| } |
| break; |
| |
| case BTA_AG_SCO_XFER_E: |
| /* close current sco connection */ |
| bta_ag_remove_sco(p_sco->p_curr_scb, true); |
| |
| /* save xfer scb */ |
| p_sco->p_xfer_scb = p_scb; |
| |
| p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; |
| break; |
| |
| case BTA_AG_SCO_CLOSE_E: |
| /* close sco connection if active */ |
| if (bta_ag_remove_sco(p_scb, true)) { |
| p_sco->state = BTA_AG_SCO_CLOSING_ST; |
| } |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* remove all listening connections */ |
| bta_ag_remove_sco(p_scb, false); |
| |
| /* If SCO was active on this scb, close it */ |
| if (p_scb == p_sco->p_curr_scb) { |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| } |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* peer closed sco; create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_OPEN_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_CLOSING_ST: |
| switch (event) { |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection (Additional channel) */ |
| if (p_scb != p_sco->p_curr_scb) { |
| bta_ag_create_sco(p_scb, false); |
| } |
| break; |
| |
| case BTA_AG_SCO_OPEN_E: |
| p_sco->state = BTA_AG_SCO_CLOSE_OP_ST; |
| break; |
| |
| case BTA_AG_SCO_XFER_E: |
| /* save xfer scb */ |
| p_sco->p_xfer_scb = p_scb; |
| |
| p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* If not closing scb, just close it */ |
| if (p_scb != p_sco->p_curr_scb) { |
| /* remove listening connection */ |
| bta_ag_remove_sco(p_scb, false); |
| } else |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* peer closed sco; create sco listen connection */ |
| bta_ag_create_sco(p_scb, false); |
| |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_CLOSING_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_CLOSE_OP_ST: |
| switch (event) { |
| case BTA_AG_SCO_CLOSE_E: |
| p_sco->state = BTA_AG_SCO_CLOSING_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* start codec negotiation */ |
| p_sco->state = BTA_AG_SCO_CODEC_ST; |
| bta_ag_codec_negotiate(p_scb); |
| break; |
| |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection (Additional channel) */ |
| if (p_scb != p_sco->p_curr_scb) { |
| bta_ag_create_sco(p_scb, false); |
| } |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_CLOSE_OP_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_CLOSE_XFER_ST: |
| switch (event) { |
| case BTA_AG_SCO_CONN_OPEN_E: |
| /* close sco connection so headset can be transferred |
| Probably entered this state from "opening state" */ |
| bta_ag_remove_sco(p_scb, true); |
| break; |
| |
| case BTA_AG_SCO_CLOSE_E: |
| /* clear xfer scb */ |
| p_sco->p_xfer_scb = nullptr; |
| |
| p_sco->state = BTA_AG_SCO_CLOSING_ST; |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| /* clear xfer scb */ |
| p_sco->p_xfer_scb = nullptr; |
| |
| p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
| break; |
| |
| case BTA_AG_SCO_CN_DONE_E: |
| case BTA_AG_SCO_CONN_CLOSE_E: { |
| /* closed sco; place old sco in listen mode, |
| take current sco out of listen, and |
| create originating sco for current */ |
| bta_ag_create_sco(p_scb, false); |
| bta_ag_remove_sco(p_sco->p_xfer_scb, false); |
| |
| #if (DISABLE_WBS == FALSE) |
| /* start codec negotiation */ |
| p_sco->state = BTA_AG_SCO_CODEC_ST; |
| tBTA_AG_SCB* p_cn_scb = p_sco->p_xfer_scb; |
| p_sco->p_xfer_scb = nullptr; |
| bta_ag_codec_negotiate(p_cn_scb); |
| #else |
| /* create sco connection to peer */ |
| bta_ag_create_sco(p_sco->p_xfer_scb, true); |
| p_sco->p_xfer_scb = nullptr; |
| p_sco->state = BTA_AG_SCO_OPENING_ST; |
| #endif |
| break; |
| } |
| |
| default: |
| log::warn("BTA_AG_SCO_CLOSE_XFER_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| case BTA_AG_SCO_SHUTTING_ST: |
| switch (event) { |
| case BTA_AG_SCO_CONN_OPEN_E: |
| /* close sco connection; wait for conn close event */ |
| bta_ag_remove_sco(p_scb, true); |
| break; |
| |
| case BTA_AG_SCO_CONN_CLOSE_E: |
| /* If last SCO instance then finish shutting down */ |
| if (!bta_ag_other_scb_open(p_scb)) { |
| p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; |
| bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| } else /* Other instance is still listening */ |
| { |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| } |
| |
| /* If SCO closed for other HS which is not being disconnected, |
| then create listen sco connection for it as scb still open */ |
| if (bta_ag_scb_open(p_scb)) { |
| bta_ag_create_sco(p_scb, false); |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| } |
| |
| if (p_scb == p_sco->p_curr_scb) { |
| p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
| p_sco->p_curr_scb = nullptr; |
| } |
| break; |
| |
| case BTA_AG_SCO_LISTEN_E: |
| /* create sco listen connection (Additional channel) */ |
| if (p_scb != p_sco->p_curr_scb) { |
| bta_ag_create_sco(p_scb, false); |
| } |
| break; |
| |
| case BTA_AG_SCO_SHUTDOWN_E: |
| if (!bta_ag_other_scb_open(p_scb)) { |
| p_sco->state = BTA_AG_SCO_SHUTDOWN_ST; |
| } else /* Other instance is still listening */ |
| { |
| p_sco->state = BTA_AG_SCO_LISTEN_ST; |
| } |
| |
| if (p_scb == p_sco->p_curr_scb) { |
| p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
| p_sco->p_curr_scb = nullptr; |
| } |
| break; |
| |
| default: |
| log::warn("BTA_AG_SCO_SHUTTING_ST: Ignoring event {}[{}]", |
| bta_ag_sco_evt_str(event), event); |
| break; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| if (p_sco->state != previous_state) { |
| log::warn( |
| "SCO_state_change: [{}(0x{:02x})]->[{}(0x{:02x})] after event " |
| "[{}(0x{:02x})]", |
| bta_ag_sco_state_str(previous_state), previous_state, |
| bta_ag_sco_state_str(p_sco->state), p_sco->state, |
| bta_ag_sco_evt_str(event), event); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_is_open |
| * |
| * Description Check if sco is open for this scb. |
| * |
| * |
| * Returns true if sco open for this scb, false otherwise. |
| * |
| ******************************************************************************/ |
| bool bta_ag_sco_is_open(tBTA_AG_SCB* p_scb) { |
| return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_ST) && |
| (bta_ag_cb.sco.p_curr_scb == p_scb)); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_is_opening |
| * |
| * Description Check if sco is in Opening state. |
| * |
| * |
| * Returns true if sco is in Opening state for this scb, false |
| * otherwise. |
| * |
| ******************************************************************************/ |
| bool bta_ag_sco_is_opening(tBTA_AG_SCB* p_scb) { |
| return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) && |
| (bta_ag_cb.sco.p_curr_scb == p_scb)); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_listen |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_listen(tBTA_AG_SCB* p_scb, |
| UNUSED_ATTR const tBTA_AG_DATA& data) { |
| log::info("{}", ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_LISTEN_E); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_open |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_open(tBTA_AG_SCB* p_scb, UNUSED_ATTR const tBTA_AG_DATA& data) { |
| if (!sco_allowed) { |
| log::info("not opening sco, by policy"); |
| return; |
| } |
| |
| p_scb->disabled_codecs = data.api_audio_open.disabled_codecs; |
| log::info("disabled_codecs = {}, sco_codec = {}", p_scb->disabled_codecs, |
| p_scb->sco_codec); |
| |
| if (p_scb->disabled_codecs & p_scb->sco_codec) { |
| tBTA_AG_PEER_CODEC updated_codec = BTM_SCO_CODEC_NONE; |
| |
| if (hfp_hal_interface::get_swb_supported() && |
| (p_scb->peer_codecs & BTM_SCO_CODEC_LC3) && |
| !(p_scb->disabled_codecs & BTM_SCO_CODEC_LC3)) { |
| updated_codec = BTM_SCO_CODEC_LC3; |
| } else if ((p_scb->peer_codecs & BTM_SCO_CODEC_MSBC) && |
| !(p_scb->disabled_codecs & BTM_SCO_CODEC_MSBC)) { |
| updated_codec = BTM_SCO_CODEC_MSBC; |
| } else { |
| updated_codec = BTM_SCO_CODEC_CVSD; |
| } |
| |
| p_scb->sco_codec = updated_codec; |
| p_scb->codec_updated = true; |
| } |
| |
| /* if another scb using sco, this is a transfer */ |
| if (bta_ag_cb.sco.p_curr_scb && bta_ag_cb.sco.p_curr_scb != p_scb) { |
| log::info("transfer {} -> {}", |
| ADDRESS_TO_LOGGABLE_STR(bta_ag_cb.sco.p_curr_scb->peer_addr), |
| ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_XFER_E); |
| } else { |
| /* else it is an open */ |
| log::info("open {}", ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_OPEN_E); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_close |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_close(tBTA_AG_SCB* p_scb, |
| UNUSED_ATTR const tBTA_AG_DATA& data) { |
| /* if scb is in use */ |
| /* sco_idx is not allocated in SCO_CODEC_ST, still need to move to listen |
| * state. */ |
| if ((p_scb->sco_idx != BTM_INVALID_SCO_INDEX) || |
| (bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST)) { |
| log::verbose("bta_ag_sco_close: sco_inx = {}", p_scb->sco_idx); |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_codec_nego |
| * |
| * Description Handles result of eSCO codec negotiation |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_codec_nego(tBTA_AG_SCB* p_scb, bool result) { |
| if (result) { |
| /* Subsequent SCO connection will skip codec negotiation */ |
| log::info("Succeeded for index 0x{:04x}, device {}", p_scb->sco_idx, |
| ADDRESS_TO_LOGGABLE_CSTR(p_scb->peer_addr)); |
| p_scb->codec_updated = false; |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_CN_DONE_E); |
| } else { |
| /* codec negotiation failed */ |
| log::info("Failed for index 0x{:04x}, device {}", p_scb->sco_idx, |
| ADDRESS_TO_LOGGABLE_CSTR(p_scb->peer_addr)); |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_shutdown |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_shutdown(tBTA_AG_SCB* p_scb, |
| UNUSED_ATTR const tBTA_AG_DATA& data) { |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_SHUTDOWN_E); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_conn_open |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_conn_open(tBTA_AG_SCB* p_scb, |
| UNUSED_ATTR const tBTA_AG_DATA& data) { |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_OPEN_E); |
| bta_sys_sco_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| |
| if (IS_FLAG_ENABLED(is_sco_managed_by_audio)) { |
| // ConfirmStreamingRequest before sends callback to java layer |
| hfp_offload_interface->ConfirmStreamingRequest(); |
| |
| bool is_controller_codec = false; |
| if (sco_config_map.find(p_scb->inuse_codec) == sco_config_map.end()) { |
| log::error("sco_config_map does not have inuse_codec={}", |
| p_scb->inuse_codec); |
| } else { |
| is_controller_codec = |
| sco_config_map[p_scb->inuse_codec].useControllerCodec; |
| } |
| |
| hfp::offload_config config{ |
| .sco_codec = p_scb->inuse_codec, |
| .connection_handle = p_scb->conn_handle, |
| .is_controller_codec = is_controller_codec, |
| .is_nrec = p_scb->nrec_enabled, |
| }; |
| hfp_offload_interface->UpdateAudioConfigToHal(config); |
| } |
| |
| /* call app callback */ |
| bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT); |
| |
| /* reset retransmission_effort_retries*/ |
| p_scb->retransmission_effort_retries = 0; |
| /* reset to mSBC T2 settings as the preferred */ |
| p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
| /* reset to LC3 T2 settings as the preferred */ |
| p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; |
| /* reset to SWB Q0 settings as the preferred */ |
| p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_conn_close |
| * |
| * Description |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_conn_close(tBTA_AG_SCB* p_scb, |
| UNUSED_ATTR const tBTA_AG_DATA& data) { |
| /* clear current scb */ |
| bta_ag_cb.sco.p_curr_scb = nullptr; |
| p_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
| const bool aptx_voice = is_hfp_aptx_voice_enabled() && |
| p_scb->codec_fallback && |
| (p_scb->sco_codec == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0); |
| log::verbose("aptx_voice={}, codec_fallback={:#x}, sco_codec={:#x}", |
| logbool(aptx_voice), p_scb->codec_fallback, p_scb->sco_codec); |
| |
| /* codec_fallback is set when AG is initiator and connection failed for mSBC. |
| * OR if codec is msbc and T2 settings failed, then retry Safe T1 settings |
| * same operations for LC3 settings */ |
| if (p_scb->svc_conn && |
| (p_scb->codec_fallback || |
| (p_scb->sco_codec == BTM_SCO_CODEC_MSBC && |
| p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1) || |
| (p_scb->sco_codec == BTM_SCO_CODEC_LC3 && |
| p_scb->codec_lc3_settings == BTA_AG_SCO_LC3_SETTINGS_T1) || |
| (IS_FLAG_ENABLED(retry_esco_with_zero_retransmission_effort) && |
| p_scb->retransmission_effort_retries == 1) || |
| aptx_voice)) { |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E); |
| } else { |
| /* Indicate if the closing of audio is because of transfer */ |
| bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E); |
| |
| bta_sys_sco_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| |
| /* if av got suspended by this call, let it resume. */ |
| /* In case call stays alive regardless of sco, av should not be affected. */ |
| if (((p_scb->call_ind == BTA_AG_CALL_INACTIVE) && |
| (p_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) || |
| (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END)) { |
| bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| } |
| |
| /* call app callback */ |
| bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); |
| p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
| p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; |
| p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function bta_ag_sco_conn_rsp |
| * |
| * Description Process the SCO connection request |
| * |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void bta_ag_sco_conn_rsp(tBTA_AG_SCB* p_scb, |
| tBTM_ESCO_CONN_REQ_EVT_DATA* p_data) { |
| bta_ag_cb.sco.is_local = false; |
| |
| log::verbose("eSCO {}, state {}", |
| controller_get_interface() |
| ->supports_enhanced_setup_synchronous_connection(), |
| bta_ag_cb.sco.state); |
| |
| if (bta_ag_cb.sco.state == BTA_AG_SCO_LISTEN_ST || |
| bta_ag_cb.sco.state == BTA_AG_SCO_CLOSE_XFER_ST || |
| bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_XFER_ST) { |
| /* tell sys to stop av if any */ |
| bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
| /* When HS initiated SCO, it cannot be WBS. */ |
| } |
| |
| /* If SCO open was initiated from HS, it must be CVSD */ |
| p_scb->inuse_codec = BTM_SCO_CODEC_NONE; |
| /* Send pending commands to create SCO connection to peer */ |
| bta_ag_create_pending_sco(p_scb, bta_ag_cb.sco.is_local); |
| } |
| |
| bool bta_ag_get_sco_offload_enabled() { |
| return hfp_hal_interface::get_offload_enabled(); |
| } |
| |
| void bta_ag_set_sco_offload_enabled(bool value) { |
| hfp_hal_interface::enable_offload(value); |
| } |
| |
| void bta_ag_set_sco_allowed(bool value) { |
| sco_allowed = value; |
| log::verbose("{}", sco_allowed ? "sco now allowed" : "sco now not allowed"); |
| } |
| |
| const RawAddress& bta_ag_get_active_device() { return active_device_addr; } |
| |
| void bta_clear_active_device() { |
| log::debug("Set bta active device to null"); |
| if (IS_FLAG_ENABLED(is_sco_managed_by_audio)) { |
| if (hfp_offload_interface && !active_device_addr.IsEmpty()) { |
| hfp_offload_interface->StopSession(); |
| } |
| } |
| active_device_addr = RawAddress::kEmpty; |
| } |
| |
| void bta_ag_api_set_active_device(const RawAddress& new_active_device) { |
| if (new_active_device.IsEmpty()) { |
| log::error("empty device"); |
| return; |
| } |
| |
| if (IS_FLAG_ENABLED(is_sco_managed_by_audio)) { |
| if (!hfp_client_interface) { |
| hfp_client_interface = std::unique_ptr<HfpInterface>(HfpInterface::Get()); |
| if (!hfp_client_interface) { |
| log::error("could not acquire audio source interface"); |
| return; |
| } |
| } |
| |
| if (!hfp_offload_interface) { |
| hfp_offload_interface = std::unique_ptr<HfpInterface::Offload>( |
| hfp_client_interface->GetOffload(get_main_thread())); |
| sco_config_map = hfp_offload_interface->GetHfpScoConfig(); |
| if (!hfp_offload_interface) { |
| log::warn("could not get offload interface"); |
| } else { |
| // start audio session if there was no previous active device |
| // if there was an active device, java layer would call disconnectAudio |
| if (active_device_addr.IsEmpty()) { |
| hfp_offload_interface->StartSession(); |
| } |
| } |
| } |
| } |
| active_device_addr = new_active_device; |
| } |