/* * Copyright (C) 2017, The Linux Foundation. All rights reserved. * Not a Contribution. * Redistribution and use in source and binary forms, with or without * modification, are permitted (subject to the limitations in the * disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of The Linux Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (C) 2016 The Android Open Source Project * * 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. * * Changes from Qualcomm Innovation Center are provided under the following license: * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause-Clear */ /** * A2DP Codecs Configuration */ #define LOG_TAG "a2dp_codec" #include "a2dp_codec_api.h" #include #include #include #include #include "a2dp_aac.h" #include "a2dp_sbc.h" #include "a2dp_vendor.h" #include "a2dp_vendor_aptx.h" #include "a2dp_vendor_aptx_hd.h" #include "a2dp_vendor_aptx_adaptive.h" #include "a2dp_vendor_ldac.h" #include "osi/include/log.h" #include "a2dp_vendor_aptx_tws.h" #include "device/include/controller.h" #include "device/include/interop.h" #include "btif_av_co.h" #include "device/include/device_iot_config.h" #include "bta/av/bta_av_int.h" #include "btif_av.h" /* The Media Type offset within the codec info byte array */ #define A2DP_MEDIA_TYPE_OFFSET 1 // Initializes the codec config. // |codec_config| is the codec config to initialize. // |codec_index| and |codec_priority| are the codec type and priority to use // for the initialization. bool mA2dp_offload_status = false; bool mA2dp_offload_scrambling_support = false; bool mA2dp_offload_44p1kFreq_support = false; bool offload_capability = false; bool sbc_offload = false; bool aac_offload = false; bool aptx_offload = false; bool aptxhd_offload = false; bool aptx_adaptive_offload = false; bool ldac_offload = false; bool aptxtws_offload = false; bool sbc_sw = false; bool aac_sw = false; bool aptx_sw = false; bool aptxhd_sw = false; bool aptx_adaptive_sw = false; bool ldac_sw = false; bool aptxtws_sw = false; bool vbr_supported = false; bool aptxadaptiver2_1_supported = false; bool aptxadaptiver2_2_supported = false; bool a2dp_aptxadaptive_src_split_tx_supported = false; std::string offload_caps = ""; static void init_btav_a2dp_codec_config( btav_a2dp_codec_config_t* codec_config, btav_a2dp_codec_index_t codec_index, btav_a2dp_codec_priority_t codec_priority) { memset(codec_config, 0, sizeof(btav_a2dp_codec_config_t)); codec_config->codec_type = codec_index; codec_config->codec_priority = codec_priority; } A2dpCodecConfig::A2dpCodecConfig(btav_a2dp_codec_index_t codec_index, const std::string& name, btav_a2dp_codec_priority_t codec_priority) : codec_index_(codec_index), name_(name), default_codec_priority_(codec_priority) { setCodecPriority(codec_priority); LOG_DEBUG(LOG_TAG, "%s: init all codec caps info", __func__); init_btav_a2dp_codec_config(&codec_config_, codec_index_, codecPriority()); init_btav_a2dp_codec_config(&codec_capability_, codec_index_, codecPriority()); init_btav_a2dp_codec_config(&codec_local_capability_, codec_index_, codecPriority()); init_btav_a2dp_codec_config(&codec_selectable_capability_, codec_index_, codecPriority()); init_btav_a2dp_codec_config(&codec_user_config_, codec_index_, BTAV_A2DP_CODEC_PRIORITY_DEFAULT); init_btav_a2dp_codec_config(&codec_audio_config_, codec_index_, BTAV_A2DP_CODEC_PRIORITY_DEFAULT); memset(ota_codec_config_, 0, sizeof(ota_codec_config_)); memset(ota_codec_peer_capability_, 0, sizeof(ota_codec_peer_capability_)); memset(ota_codec_peer_config_, 0, sizeof(ota_codec_peer_config_)); } A2dpCodecConfig::~A2dpCodecConfig() {} void A2dpCodecConfig::setCodecPriority( btav_a2dp_codec_priority_t codec_priority) { if (codec_priority == BTAV_A2DP_CODEC_PRIORITY_DEFAULT) { // Compute the default codec priority setDefaultCodecPriority(); } else { codec_priority_ = codec_priority; } LOG_DEBUG(LOG_TAG, "%s: codec_priority_: %d", __func__, codec_priority_); codec_config_.codec_priority = codec_priority_; } void A2dpCodecConfig::setDefaultCodecPriority() { LOG_DEBUG(LOG_TAG, "%s: default_codec_priority_: %d", __func__, default_codec_priority_); if (default_codec_priority_ != BTAV_A2DP_CODEC_PRIORITY_DEFAULT) { codec_priority_ = default_codec_priority_; } else { // Compute the default codec priority LOG_DEBUG(LOG_TAG, "%s: Compute the default codec priority", __func__); uint32_t priority = 1000 * (codec_index_ + 1) + 1; codec_priority_ = static_cast(priority); } LOG_DEBUG(LOG_TAG, "%s: codec_priority_: %d", __func__, codec_priority_); codec_config_.codec_priority = codec_priority_; } A2dpCodecConfig* A2dpCodecConfig::createCodec( btav_a2dp_codec_index_t codec_index, btav_a2dp_codec_priority_t codec_priority) { LOG_DEBUG(LOG_TAG, "%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index)); A2dpCodecConfig* codec_config = nullptr; switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: codec_config = new A2dpCodecConfigSbc(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SINK_SBC: codec_config = new A2dpCodecConfigSbcSink(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: codec_config = new A2dpCodecConfigAac(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: codec_config = new A2dpCodecConfigAptx(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: codec_config = new A2dpCodecConfigAptxHd(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE: codec_config = new A2dpCodecConfigAptxAdaptive(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: codec_config = new A2dpCodecConfigLdac(codec_priority); break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_TWS: codec_config = new A2dpCodecConfigAptxTWS(codec_priority); break; // Add a switch statement for each vendor-specific codec case BTAV_A2DP_CODEC_INDEX_MAX: break; default: break; } if (codec_config != nullptr) { if (!codec_config->init()) { delete codec_config; codec_config = nullptr; } } return codec_config; } bool A2dpCodecConfig::getCodecSpecificConfig(tBT_A2DP_OFFLOAD* p_a2dp_offload) { std::lock_guard lock(codec_mutex_); uint8_t codec_config[AVDT_CODEC_SIZE]; uint32_t vendor_id; uint16_t codec_id; memset(p_a2dp_offload->codec_info, 0, sizeof(p_a2dp_offload->codec_info)); if (!A2DP_IsSourceCodecValid(ota_codec_config_)) { return false; } memcpy(codec_config, ota_codec_config_, sizeof(ota_codec_config_)); tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(codec_config); switch (codec_type) { case A2DP_MEDIA_CT_SBC: p_a2dp_offload->codec_info[0] = codec_config[3]; // samplefreq | channelmode p_a2dp_offload->codec_info[1] = codec_config[4]; // blk_len | subbands | Alloc Method p_a2dp_offload->codec_info[2] = codec_config[5]; // Min bit pool p_a2dp_offload->codec_info[3] = codec_config[6]; // Max bit pool break; case A2DP_MEDIA_CT_AAC: p_a2dp_offload->codec_info[0] = codec_config[3]; // object type p_a2dp_offload->codec_info[1] = codec_config[6]; // VBR | BR break; case A2DP_MEDIA_CT_NON_A2DP: vendor_id = A2DP_VendorCodecGetVendorId(codec_config); codec_id = A2DP_VendorCodecGetCodecId(codec_config); p_a2dp_offload->codec_info[0] = (vendor_id & 0x000000FF); p_a2dp_offload->codec_info[1] = (vendor_id & 0x0000FF00) >> 8; p_a2dp_offload->codec_info[2] = (vendor_id & 0x00FF0000) >> 16; p_a2dp_offload->codec_info[3] = (vendor_id & 0xFF000000) >> 24; p_a2dp_offload->codec_info[4] = (codec_id & 0x000000FF); p_a2dp_offload->codec_info[5] = (codec_id & 0x0000FF00) >> 8; if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { if (codec_config_.codec_specific_1 == 0) { // default is 0, ABR p_a2dp_offload->codec_info[6] = A2DP_LDAC_QUALITY_ABR_OFFLOAD; // ABR in offload } else { switch (codec_config_.codec_specific_1 % 10) { case 0: p_a2dp_offload->codec_info[6] = A2DP_LDAC_QUALITY_HIGH; // High bitrate break; case 1: p_a2dp_offload->codec_info[6] = A2DP_LDAC_QUALITY_MID; // Mid birate break; case 2: p_a2dp_offload->codec_info[6] = A2DP_LDAC_QUALITY_LOW; // Low birate break; case 3: FALLTHROUGH_INTENDED; /* FALLTHROUGH */ default: p_a2dp_offload->codec_info[6] = A2DP_LDAC_QUALITY_ABR_OFFLOAD; // ABR in offload break; } } p_a2dp_offload->codec_info[7] = codec_config[10]; // LDAC specific channel mode LOG_VERBOSE(LOG_TAG, "%s: Ldac specific channelmode =%d", __func__, p_a2dp_offload->codec_info[7]); } break; default: break; } return true; } bool A2dpCodecConfig::isValid() const { return true; } bool A2dpCodecConfig::copyOutOtaCodecConfig(uint8_t* p_codec_info) { std::lock_guard lock(codec_mutex_); // TODO: We should use a mechanism to verify codec config, // not codec capability. if (!A2DP_IsSourceCodecValid(ota_codec_config_)) { return false; } memcpy(p_codec_info, ota_codec_config_, sizeof(ota_codec_config_)); return true; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecConfig() { std::lock_guard lock(codec_mutex_); // TODO: We should check whether the codec config is valid return codec_config_; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecCapability() { std::lock_guard lock(codec_mutex_); // TODO: We should check whether the codec capability is valid return codec_capability_; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecLocalCapability() { std::lock_guard lock(codec_mutex_); // TODO: We should check whether the codec capability is valid return codec_local_capability_; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecSelectableCapability() { std::lock_guard lock(codec_mutex_); // TODO: We should check whether the codec capability is valid return codec_selectable_capability_; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecUserConfig() { std::lock_guard lock(codec_mutex_); return codec_user_config_; } btav_a2dp_codec_config_t A2dpCodecConfig::getCodecAudioConfig() { std::lock_guard lock(codec_mutex_); return codec_audio_config_; } uint8_t A2dpCodecConfig::getAudioBitsPerSample() { std::lock_guard lock(codec_mutex_); switch (codec_config_.bits_per_sample) { case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16: return 16; case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24: return 24; case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32: return 32; case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE: break; } return 0; } bool A2dpCodecConfig::isCodecConfigEmpty( const btav_a2dp_codec_config_t& codec_config) { return ( (codec_config.codec_priority == BTAV_A2DP_CODEC_PRIORITY_DEFAULT) && (codec_config.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) && (codec_config.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) && (codec_config.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) && (codec_config.codec_specific_1 == 0) && (codec_config.codec_specific_2 == 0) && (codec_config.codec_specific_3 == 0) && (codec_config.codec_specific_4 == 0)); } bool A2dpCodecConfig::setCodecUserConfig( const btav_a2dp_codec_config_t& codec_user_config, const btav_a2dp_codec_config_t& codec_audio_config, const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, const uint8_t* p_peer_codec_info, bool is_capability, uint8_t* p_result_codec_config, bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) { std::lock_guard lock(codec_mutex_); *p_restart_input = false; *p_restart_output = false; *p_config_updated = false; // Save copies of the current codec config, and the OTA codec config, so they // can be compared for changes. btav_a2dp_codec_config_t saved_codec_config = getCodecConfig(); LOG_DEBUG(LOG_TAG, "%s: saved_codec_config: ", __func__); print_codec_parameters(saved_codec_config); print_codec_config(ota_codec_config_); uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE]; memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_)); uint8_t zero[AVDT_CODEC_SIZE] = {0}; btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_; LOG_DEBUG(LOG_TAG, "%s: saved_codec_user_config: ", __func__); print_codec_parameters(saved_codec_user_config); codec_user_config_ = codec_user_config; LOG_DEBUG(LOG_TAG, "%s: codec_user_config_: ", __func__); print_codec_parameters(codec_user_config_); btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_; LOG_DEBUG(LOG_TAG, "%s: saved_codec_audio_config: ", __func__); print_codec_parameters(saved_codec_audio_config); codec_audio_config_ = codec_audio_config; LOG_DEBUG(LOG_TAG, "%s: codec_audio_config_: ", __func__); print_codec_parameters(codec_audio_config_); bool success = setCodecConfig(p_peer_codec_info, is_capability, p_result_codec_config); LOG_DEBUG(LOG_TAG, "%s: success: %d, is_capability:%d", __func__, success, is_capability); if (!success) { // Restore the local copy of the user and audio config codec_user_config_ = saved_codec_user_config; codec_audio_config_ = saved_codec_audio_config; return false; } // // The input (audio data) should be restarted if the audio format has changed // btav_a2dp_codec_config_t new_codec_config = getCodecConfig(); LOG_DEBUG(LOG_TAG, "%s: new_codec_config: ", __func__); print_codec_parameters(new_codec_config); if ((saved_codec_config.sample_rate != new_codec_config.sample_rate) || (saved_codec_config.bits_per_sample != new_codec_config.bits_per_sample) || (saved_codec_config.channel_mode != new_codec_config.channel_mode)) { *p_restart_input = true; } // // The output (the connection) should be restarted if OTA codec config // has changed and OTA codec has been built. // if (!A2DP_CodecEquals(saved_ota_codec_config, p_result_codec_config) && memcmp(zero, saved_ota_codec_config, sizeof(zero))) { *p_restart_output = true; } LOG_DEBUG(LOG_TAG, "%s: *p_restart_input:%d, *p_restart_output:%d, *p_config_updated:%d", __func__, *p_restart_input, *p_restart_output, *p_config_updated); bool encoder_restart_input = *p_restart_input; bool encoder_restart_output = *p_restart_output; bool encoder_config_updated = *p_config_updated; if (updateEncoderUserConfig(p_peer_params, &encoder_restart_input, &encoder_restart_output, &encoder_config_updated)) { LOG_DEBUG(LOG_TAG, "%s: updated encoder user config:", __func__); if (encoder_restart_input) *p_restart_input = true; if (encoder_restart_output) *p_restart_output = true; if (encoder_config_updated) *p_config_updated = true; } if (*p_restart_input || *p_restart_output) *p_config_updated = true; return true; } bool A2dpCodecConfig::codecConfigIsValid( const btav_a2dp_codec_config_t& codec_config) { return (codec_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) && (codec_config.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) && (codec_config.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) && (codec_config.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE); } std::string A2dpCodecConfig::codecConfig2Str( const btav_a2dp_codec_config_t& codec_config) { std::string result; if (!codecConfigIsValid(codec_config)) return "Invalid"; result.append("Rate="); result.append(codecSampleRate2Str(codec_config.sample_rate)); result.append(" Bits="); result.append(codecBitsPerSample2Str(codec_config.bits_per_sample)); result.append(" Mode="); result.append(codecChannelMode2Str(codec_config.channel_mode)); return result; } std::string A2dpCodecConfig::codecSampleRate2Str( btav_a2dp_codec_sample_rate_t codec_sample_rate) { std::string result; if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_44100) { if (!result.empty()) result += "|"; result += "44100"; } if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_48000) { if (!result.empty()) result += "|"; result += "48000"; } if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_88200) { if (!result.empty()) result += "|"; result += "88200"; } if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_96000) { if (!result.empty()) result += "|"; result += "96000"; } if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_176400) { if (!result.empty()) result += "|"; result += "176400"; } if (codec_sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_192000) { if (!result.empty()) result += "|"; result += "192000"; } if (result.empty()) { std::stringstream ss; ss << "UnknownSampleRate(0x" << std::hex << codec_sample_rate << ")"; ss >> result; } return result; } std::string A2dpCodecConfig::codecBitsPerSample2Str( btav_a2dp_codec_bits_per_sample_t codec_bits_per_sample) { std::string result; if (codec_bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) { if (!result.empty()) result += "|"; result += "16"; } if (codec_bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) { if (!result.empty()) result += "|"; result += "24"; } if (codec_bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) { if (!result.empty()) result += "|"; result += "32"; } if (result.empty()) { std::stringstream ss; ss << "UnknownBitsPerSample(0x" << std::hex << codec_bits_per_sample << ")"; ss >> result; } return result; } std::string A2dpCodecConfig::getOffloadCaps() { return offload_caps; } std::string A2dpCodecConfig::codecChannelMode2Str( btav_a2dp_codec_channel_mode_t codec_channel_mode) { std::string result; if (codec_channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_MONO) { if (!result.empty()) result += "|"; result += "MONO"; } if (codec_channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO) { if (!result.empty()) result += "|"; result += "STEREO"; } if (result.empty()) { std::stringstream ss; ss << "UnknownChannelMode(0x" << std::hex << codec_channel_mode << ")"; ss >> result; } return result; } void A2dpCodecConfig::debug_codec_dump(int fd) { if(fd == -1) { std::string result; ALOGE("\nA2DP %s State:\n", name().c_str()); ALOGE(" Priority: %d\n", codecPriority()); ALOGE(" Encoder interval (ms): %" PRIu64 "\n", encoderIntervalMs()); result = codecConfig2Str(getCodecConfig()); ALOGE(" Config: %s\n", result.c_str()); result = codecConfig2Str(getCodecSelectableCapability()); ALOGE(" Selectable: %s\n", result.c_str()); result = codecConfig2Str(getCodecLocalCapability()); ALOGE(" Local capability: %s\n", result.c_str()); } else{ std::string result; dprintf(fd, "\nA2DP %s State:\n", name().c_str()); dprintf(fd, " Priority: %d\n", codecPriority()); dprintf(fd, " Encoder interval (ms): %" PRIu64 "\n", encoderIntervalMs()); result = codecConfig2Str(getCodecConfig()); dprintf(fd, " Config: %s\n", result.c_str()); result = codecConfig2Str(getCodecSelectableCapability()); dprintf(fd, " Selectable: %s\n", result.c_str()); result = codecConfig2Str(getCodecLocalCapability()); dprintf(fd, " Local capability: %s\n", result.c_str()); } } // // Compares two codecs |lhs| and |rhs| based on their priority. // Returns true if |lhs| has higher priority (larger priority value). // If |lhs| and |rhs| have same priority, the unique codec index is used // as a tie-breaker: larger codec index value means higher priority. // static bool compare_codec_priority(const A2dpCodecConfig* lhs, const A2dpCodecConfig* rhs) { if (lhs->codecPriority() > rhs->codecPriority()) return true; if (lhs->codecPriority() < rhs->codecPriority()) return false; return (lhs->codecIndex() > rhs->codecIndex()); } A2dpCodecs::A2dpCodecs( const std::vector& codec_priorities) : current_codec_config_(nullptr) { for (auto config : codec_priorities) { codec_priorities_.insert( std::make_pair(config.codec_type, config.codec_priority)); } } A2dpCodecs::~A2dpCodecs() { std::unique_lock lock(codec_mutex_); for (const auto& iter : indexed_codecs_) { delete iter.second; } for (const auto& iter : disabled_codecs_) { delete iter.second; } lock.unlock(); } bool A2dpCodecs::init(bool isMulticastEnabled) { LOG_DEBUG(LOG_TAG, "%s", __func__); std::lock_guard lock(codec_mutex_); ordered_source_codecs_.clear(); ordered_sink_codecs_.clear(); indexed_codecs_.clear(); disabled_codecs_.clear(); for (int i = BTAV_A2DP_CODEC_INDEX_MIN; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) { btav_a2dp_codec_index_t codec_index = static_cast(i); if ((isMulticastEnabled == true) && (codec_index != BTAV_A2DP_CODEC_INDEX_SOURCE_SBC)) { LOG_INFO(LOG_TAG, "%s: Selecting SBC codec as multicast is enabled", __func__); continue; } // Select the codec priority if explicitly configured btav_a2dp_codec_priority_t codec_priority = BTAV_A2DP_CODEC_PRIORITY_DEFAULT; auto cp_iter = codec_priorities_.find(codec_index); if (cp_iter != codec_priorities_.end()) { codec_priority = cp_iter->second; } A2dpCodecConfig* codec_config = A2dpCodecConfig::createCodec(codec_index, codec_priority); if (codec_config == nullptr) continue; if (codec_priority != BTAV_A2DP_CODEC_PRIORITY_DEFAULT) { LOG_INFO(LOG_TAG, "%s: updated %s codec priority to %d", __func__, codec_config->name().c_str(), codec_priority); } // Test if the codec is disabled if (codec_config->codecPriority() == BTAV_A2DP_CODEC_PRIORITY_DISABLED) { disabled_codecs_.insert(std::make_pair(codec_index, codec_config)); continue; } indexed_codecs_.insert(std::make_pair(codec_index, codec_config)); if (codec_index < BTAV_A2DP_QVA_CODEC_INDEX_SOURCE_MAX) { ordered_source_codecs_.push_back(codec_config); ordered_source_codecs_.sort(compare_codec_priority); } else { ordered_sink_codecs_.push_back(codec_config); ordered_sink_codecs_.sort(compare_codec_priority); } } if (ordered_source_codecs_.empty()) { LOG_ERROR(LOG_TAG, "%s: no Source codecs were initialized", __func__); } else { for (auto iter : ordered_source_codecs_) { LOG_INFO(LOG_TAG, "%s: initialized Source codec %s", __func__, iter->name().c_str()); } } if (ordered_sink_codecs_.empty()) { LOG_ERROR(LOG_TAG, "%s: no Sink codecs were initialized", __func__); } else { for (auto iter : ordered_sink_codecs_) { LOG_INFO(LOG_TAG, "%s: initialized Sink codec %s", __func__, iter->name().c_str()); } } return (!ordered_source_codecs_.empty() && !ordered_sink_codecs_.empty()); } A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig( const uint8_t* p_codec_info) { std::lock_guard lock(codec_mutex_); btav_a2dp_codec_index_t codec_index = A2DP_SourceCodecIndex(p_codec_info); if (codec_index == BTAV_A2DP_CODEC_INDEX_MAX) return nullptr; auto iter = indexed_codecs_.find(codec_index); if (iter == indexed_codecs_.end()) return nullptr; return iter->second; } bool A2dpCodecs::setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability, uint8_t* p_result_codec_config, bool select_current_codec) { std::lock_guard lock(codec_mutex_); A2dpCodecConfig* a2dp_codec_config = findSourceCodecConfig(p_peer_codec_info); if (a2dp_codec_config == nullptr) return false; if (!a2dp_codec_config->setCodecConfig(p_peer_codec_info, is_capability, p_result_codec_config)) { LOG_DEBUG(LOG_TAG, "%s: Codec config didn't set, return false", __func__); return false; } LOG_DEBUG(LOG_TAG, "%s: select_current_codec:%d", __func__,select_current_codec); if (select_current_codec) { current_codec_config_ = a2dp_codec_config; } return true; } bool A2dpCodecs::setCodecUserConfig( const btav_a2dp_codec_config_t& codec_user_config, const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, const uint8_t* p_peer_sink_capabilities, uint8_t* p_result_codec_config, bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) { std::lock_guard lock(codec_mutex_); btav_a2dp_codec_config_t codec_audio_config; A2dpCodecConfig* a2dp_codec_config = nullptr; A2dpCodecConfig* last_codec_config = current_codec_config_; *p_restart_input = false; *p_restart_output = false; *p_config_updated = false; LOG_DEBUG(LOG_TAG, "%s: configuring codec_user_config: ", __func__); print_codec_parameters(codec_user_config); if (codec_user_config.codec_type < BTAV_A2DP_CODEC_INDEX_MAX) { auto iter = indexed_codecs_.find(codec_user_config.codec_type); if (iter == indexed_codecs_.end()) goto fail; a2dp_codec_config = iter->second; } else { // Update the default codec a2dp_codec_config = current_codec_config_; } if (a2dp_codec_config == nullptr) goto fail; // Reuse the existing codec audio config codec_audio_config = a2dp_codec_config->getCodecAudioConfig(); if (!a2dp_codec_config->setCodecUserConfig( codec_user_config, codec_audio_config, p_peer_params, p_peer_sink_capabilities, true, p_result_codec_config, p_restart_input, p_restart_output, p_config_updated)) { goto fail; } // Update the codec priorities, and eventually restart the connection // if a new codec needs to be selected. do { // Update the codec priority btav_a2dp_codec_priority_t old_priority = a2dp_codec_config->codecPriority(); btav_a2dp_codec_priority_t new_priority = codec_user_config.codec_priority; LOG_DEBUG(LOG_TAG, "%s: old_priority: %d new_priority: %d", __func__, old_priority, new_priority); a2dp_codec_config->setCodecPriority(new_priority); // Get the actual (recomputed) priority new_priority = a2dp_codec_config->codecPriority(); LOG_DEBUG(LOG_TAG, "%s: computed new_priority: %d", __func__, new_priority); // Check if there was no previous codec if (last_codec_config == nullptr) { LOG_DEBUG(LOG_TAG, "%s: last codec config is null", __func__); current_codec_config_ = a2dp_codec_config; *p_restart_output = true; break; } // Check if the priority of the current codec was updated if (a2dp_codec_config == last_codec_config) { LOG_DEBUG(LOG_TAG, "%s: both last and current config are equal", __func__); if (old_priority == new_priority) { LOG_DEBUG(LOG_TAG, "%s: No change in priority", __func__); break; // No change in priority } *p_config_updated = true; if (new_priority < old_priority) { // The priority has become lower - restart the connection to // select a new codec. *p_restart_output = true; } break; } if (new_priority <= old_priority) { // No change in priority, or the priority has become lower. // This wasn't the current codec, so we shouldn't select a new codec. if (*p_restart_input || *p_restart_output || (old_priority != new_priority)) { *p_config_updated = true; } *p_restart_input = false; *p_restart_output = false; break; } *p_config_updated = true; if (new_priority >= last_codec_config->codecPriority()) { // The new priority is higher than the current codec. Restart the // connection to select a new codec. LOG_DEBUG(LOG_TAG, "%s: new_priority is equal or higher", __func__); current_codec_config_ = a2dp_codec_config; last_codec_config->setDefaultCodecPriority(); *p_restart_output = true; } } while (false); ordered_source_codecs_.sort(compare_codec_priority); if (*p_restart_input || *p_restart_output) *p_config_updated = true; LOG_DEBUG(LOG_TAG, "%s: Configured: restart_input = %d restart_output = %d " "config_updated = %d", __func__, *p_restart_input, *p_restart_output, *p_config_updated); return true; fail: current_codec_config_ = last_codec_config; return false; } bool A2dpCodecs::setCodecAudioConfig( const btav_a2dp_codec_config_t& codec_audio_config, const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, const uint8_t* p_peer_sink_capabilities, uint8_t* p_result_codec_config, bool* p_restart_output, bool* p_config_updated) { std::lock_guard lock(codec_mutex_); btav_a2dp_codec_config_t codec_user_config; A2dpCodecConfig* a2dp_codec_config = current_codec_config_; *p_restart_output = false; *p_config_updated = false; if (a2dp_codec_config == nullptr) return false; // Reuse the existing codec user config codec_user_config = a2dp_codec_config->getCodecUserConfig(); LOG_DEBUG(LOG_TAG, "%s: codec_user_config: Reuse the existing codec user config", __func__); print_codec_parameters(codec_user_config); bool restart_input = false; // Flag ignored - input was just restarted if (!a2dp_codec_config->setCodecUserConfig( codec_user_config, codec_audio_config, p_peer_params, p_peer_sink_capabilities, true, p_result_codec_config, &restart_input, p_restart_output, p_config_updated)) { return false; } return true; } bool A2dpCodecs::setCodecOtaConfig( const uint8_t* p_ota_codec_config, const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, uint8_t* p_result_codec_config, bool* p_restart_input, bool* p_restart_output, bool* p_config_updated, RawAddress addr) { std::lock_guard lock(codec_mutex_); btav_a2dp_codec_index_t codec_type; btav_a2dp_codec_config_t codec_user_config; btav_a2dp_codec_config_t codec_audio_config; A2dpCodecConfig* a2dp_codec_config = nullptr; A2dpCodecConfig* last_codec_config = current_codec_config_; *p_restart_input = false; *p_restart_output = false; *p_config_updated = false; LOG_DEBUG(LOG_TAG, "%s: p_ota_codec_config[%x:%x:%x:%x:%x:%x]", __func__, p_ota_codec_config[1], p_ota_codec_config[2], p_ota_codec_config[3], p_ota_codec_config[4], p_ota_codec_config[5], p_ota_codec_config[6]); // Check whether the codec config for the same codec is explicitly configured // by user configuration. If yes, then the OTA codec configuration is // ignored. codec_type = A2DP_SourceCodecIndex(p_ota_codec_config); if (codec_type == BTAV_A2DP_CODEC_INDEX_MAX) { LOG_WARN(LOG_TAG, "%s: ignoring peer OTA codec configuration: " "invalid codec", __func__); goto fail; // Invalid codec } else { auto iter = indexed_codecs_.find(codec_type); if (iter == indexed_codecs_.end()) { LOG_WARN(LOG_TAG, "%s: cannot find codec configuration for peer OTA codec %s", __func__, A2DP_CodecName(p_ota_codec_config)); goto fail; } a2dp_codec_config = iter->second; } current_codec_config_ = a2dp_codec_config; // Reuse the existing codec user config and codec audio config codec_audio_config = a2dp_codec_config->getCodecAudioConfig(); codec_user_config = a2dp_codec_config->getCodecUserConfig(); //Assumption is whenever incoming connection done set_config on SBC, //marking it as high priority when HD option has been disabled. //So that form DEV-UI optionalcodecenable API would trigger by //lowering its priority and in setCodecUserConfig() proiority //would get differ which makes p_restart_output hets to true //results in reconfig to high priority codec. //codec_user_config is stack variable where it would negotiate. //setCodecPriority() API would set codec_priority_ which would get populate //to BT App. LOG_DEBUG(LOG_TAG, "%s: codec_name: %s, add: %s ", __func__, A2DP_CodecName(p_ota_codec_config), addr.ToString().c_str()); if (btif_av_peer_prefers_mandatory_codec(addr) && !strcmp(A2DP_CodecName(p_ota_codec_config), "SBC")) { LOG_DEBUG(LOG_TAG, "%s: overwriting SBC priority to high ", __func__); btav_a2dp_codec_config_t high_priority_mandatory{ .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST, // Using default settings for those untouched fields }; codec_user_config = high_priority_mandatory; a2dp_codec_config->setCodecPriority(BTAV_A2DP_CODEC_PRIORITY_HIGHEST); } LOG_DEBUG(LOG_TAG, "%s: codec_user_config After fetching: ", __func__); print_codec_parameters(codec_user_config); LOG_DEBUG(LOG_TAG, "%s: codec_audio_config After fetching: ", __func__); print_codec_parameters(codec_audio_config); if (!a2dp_codec_config->setCodecUserConfig( codec_user_config, codec_audio_config, p_peer_params, p_ota_codec_config, false, p_result_codec_config, p_restart_input, p_restart_output, p_config_updated)) { LOG_WARN(LOG_TAG, "%s: cannot set codec configuration for peer OTA codec %s", __func__, A2DP_CodecName(p_ota_codec_config)); goto fail; } CHECK(current_codec_config_ != nullptr); LOG_WARN(LOG_TAG, "%s: *p_restart_input: %d, *p_restart_output: %d", __func__, *p_restart_input, *p_restart_output); if (*p_restart_input || *p_restart_output) *p_config_updated = true; return true; fail: current_codec_config_ = last_codec_config; return false; } bool A2dpCodecs::getCodecConfigAndCapabilities( btav_a2dp_codec_config_t* p_codec_config, std::vector* p_codecs_local_capabilities, std::vector* p_codecs_selectable_capabilities) { std::lock_guard lock(codec_mutex_); LOG_DEBUG(LOG_TAG, "%s: Recursive mutex lock acquired", __func__); if (current_codec_config_ != nullptr) { LOG_DEBUG(LOG_TAG, "%s: current_codec_config_ not null, getCodecConfig()", __func__); *p_codec_config = current_codec_config_->getCodecConfig(); } else { btav_a2dp_codec_config_t codec_config; memset(&codec_config, 0, sizeof(codec_config)); *p_codec_config = codec_config; } std::vector codecs_capabilities; for (auto codec : orderedSourceCodecs()) { codecs_capabilities.push_back(codec->getCodecLocalCapability()); } *p_codecs_local_capabilities = codecs_capabilities; codecs_capabilities.clear(); for (auto codec : orderedSourceCodecs()) { btav_a2dp_codec_config_t codec_capability = codec->getCodecSelectableCapability(); // Don't add entries that cannot be used if ((codec_capability.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) || (codec_capability.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) || (codec_capability.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE)) { continue; } codecs_capabilities.push_back(codec_capability); } *p_codecs_selectable_capabilities = codecs_capabilities; LOG_DEBUG(LOG_TAG, "%s: Recursive mutex lock released", __func__); return true; } void A2dpCodecs::debug_codec_dump(int fd) { std::lock_guard lock(codec_mutex_); dprintf(fd, "\nA2DP Codecs State:\n"); // Print the current codec name if (current_codec_config_ != nullptr) { dprintf(fd, " Current Codec: %s\n", current_codec_config_->name().c_str()); } else { dprintf(fd, " Current Codec: None\n"); } // Print the codec-specific state for (auto codec_config : ordered_source_codecs_) { codec_config->debug_codec_dump(fd); } } bool A2dpCodecConfig::updateCodecConfig(const btav_a2dp_codec_config_t& update_codec_config, bool audio_update) { if (audio_update == true) codec_config_ = update_codec_config; LOG_DEBUG(LOG_TAG, "%s: Updated audio config ", __func__); return true; } bool A2dpCodecs::updateCodecConfig(const btav_a2dp_codec_config_t& update_codec_config) { A2dpCodecConfig* a2dp_codec_config_update = nullptr; a2dp_codec_config_update = current_codec_config_; bool ret = a2dp_codec_config_update->updateCodecConfig(update_codec_config, true); return ret; } #if (BT_IOT_LOGGING_ENABLED == TRUE) int A2DP_IotGetPeerSinkCodecType(const uint8_t* p_codec_info) { int peer_codec_type = 0; tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_SBC; break; case A2DP_MEDIA_CT_NON_A2DP: { uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info); uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s codec_id = %d", __func__, codec_id ); LOG_VERBOSE(LOG_TAG, "%s vendor_id = %x", __func__, vendor_id ); if (codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH && vendor_id == A2DP_APTX_VENDOR_ID) { peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTX; } else if (codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH && vendor_id == A2DP_APTX_HD_VENDOR_ID) { peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTXHD; } else if (codec_id == A2DP_APTX_ADAPTIVE_CODEC_ID_BLUETOOTH && vendor_id == A2DP_APTX_ADAPTIVE_VENDOR_ID) { peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTXADAPTIVE; } else if (codec_id == A2DP_APTX_TWS_CODEC_ID_BLUETOOTH && vendor_id == A2DP_APTX_TWS_VENDOR_ID) { peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTXTWS; } else if (codec_id == A2DP_LDAC_CODEC_ID && vendor_id == A2DP_LDAC_VENDOR_ID) { peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_LDAC; } break; } case A2DP_MEDIA_CT_AAC: peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_AAC; break; default: break; } return peer_codec_type; } #endif tA2DP_CODEC_TYPE A2DP_GetCodecType(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = (tA2DP_CODEC_TYPE)(p_codec_info[AVDT_CODEC_TYPE_INDEX]); LOG_DEBUG(LOG_TAG, "%s: codec_type: %d", __func__, codec_type); return codec_type; } bool A2DP_IsSourceCodecValid(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsSourceCodecValidSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsSourceCodecValidAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorSourceCodecValid(p_codec_info); default: break; } return false; } bool A2DP_IsSinkCodecValid(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsSinkCodecValidSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsSinkCodecValidAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorSinkCodecValid(p_codec_info); default: break; } return false; } bool A2DP_IsPeerSourceCodecValid(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsPeerSourceCodecValidSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsPeerSourceCodecValidAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorPeerSourceCodecValid(p_codec_info); default: break; } return false; } bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_DEBUG(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsPeerSinkCodecValidSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsPeerSinkCodecValidAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorPeerSinkCodecValid(p_codec_info); default: break; } return false; } bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsSinkCodecSupportedSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsSinkCodecSupportedAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorSinkCodecSupported(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } bool A2DP_IsPeerSourceCodecSupported(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsPeerSourceCodecSupportedSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsPeerSourceCodecSupportedAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_IsVendorPeerSourceCodecSupported(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } void A2DP_InitDefaultCodec(uint8_t* p_codec_info) { A2DP_InitDefaultCodecSbc(p_codec_info); } tA2DP_STATUS A2DP_BuildSrc2SinkConfig(const uint8_t* p_src_cap, uint8_t* p_pref_cfg) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_src_cap); LOG_DEBUG(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_BuildSrc2SinkConfigSbc(p_src_cap, p_pref_cfg); case A2DP_MEDIA_CT_AAC: return A2DP_BuildSrc2SinkConfigAac(p_src_cap, p_pref_cfg); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorBuildSrc2SinkConfig(p_src_cap, p_pref_cfg); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return A2DP_NS_CODEC_TYPE; } bool A2DP_UsesRtpHeader(bool content_protection_enabled, const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); if (codec_type != A2DP_MEDIA_CT_NON_A2DP) return true; return A2DP_VendorUsesRtpHeader(content_protection_enabled, p_codec_info); } uint8_t A2DP_GetMediaType(const uint8_t* p_codec_info) { uint8_t media_type = (p_codec_info[A2DP_MEDIA_TYPE_OFFSET] >> 4) & 0x0f; return media_type; } const char* A2DP_CodecName(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_CodecNameSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_CodecNameAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorCodecName(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return "UNKNOWN CODEC"; } bool A2DP_CodecTypeEquals(const uint8_t* p_codec_info_a, const uint8_t* p_codec_info_b) { tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a); tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b); if (codec_type_a != codec_type_b) return false; switch (codec_type_a) { case A2DP_MEDIA_CT_SBC: return A2DP_CodecTypeEqualsSbc(p_codec_info_a, p_codec_info_b); case A2DP_MEDIA_CT_AAC: return A2DP_CodecTypeEqualsAac(p_codec_info_a, p_codec_info_b); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorCodecTypeEquals(p_codec_info_a, p_codec_info_b); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a); return false; } bool A2DP_CodecEquals(const uint8_t* p_codec_info_a, const uint8_t* p_codec_info_b) { tA2DP_CODEC_TYPE codec_type_a = A2DP_GetCodecType(p_codec_info_a); tA2DP_CODEC_TYPE codec_type_b = A2DP_GetCodecType(p_codec_info_b); if (codec_type_a != codec_type_b) return false; switch (codec_type_a) { case A2DP_MEDIA_CT_SBC: return A2DP_CodecEqualsSbc(p_codec_info_a, p_codec_info_b); case A2DP_MEDIA_CT_AAC: return A2DP_CodecEqualsAac(p_codec_info_a, p_codec_info_b); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorCodecEquals(p_codec_info_a, p_codec_info_b); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type_a); return false; } int A2DP_GetTrackBitRate(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetBitRate(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } int A2DP_GetTrackSampleRate(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetTrackSampleRateSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_GetTrackSampleRateAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetTrackSampleRate(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } int A2DP_GetTrackChannelCount(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetTrackChannelCountSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_GetTrackChannelCountAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetTrackChannelCount(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } int A2DP_GetTrackBitsPerSample(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return 16; case A2DP_MEDIA_CT_AAC: return 16; case A2DP_MEDIA_CT_NON_A2DP: { uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info); uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info); // Check for aptX if (vendor_id == A2DP_APTX_VENDOR_ID && codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) { return 16; } // Check for aptX-HD if (vendor_id == A2DP_APTX_HD_VENDOR_ID && codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) { return 24; } // Check for LDAC if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { return 32; } } break; default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } int A2DP_GetSinkTrackChannelType(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetSinkTrackChannelTypeSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_GetSinkTrackChannelTypeAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetSinkTrackChannelType(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } int A2DP_GetSinkFramesCountToProcess(uint64_t time_interval_ms, const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetSinkFramesCountToProcessSbc(time_interval_ms, p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_GetSinkFramesCountToProcessAac(time_interval_ms, p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetSinkFramesCountToProcess(time_interval_ms, p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return -1; } bool A2DP_GetPacketTimestamp(const uint8_t* p_codec_info, const uint8_t* p_data, uint32_t* p_timestamp) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetPacketTimestampSbc(p_codec_info, p_data, p_timestamp); case A2DP_MEDIA_CT_AAC: return A2DP_GetPacketTimestampAac(p_codec_info, p_data, p_timestamp); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetPacketTimestamp(p_codec_info, p_data, p_timestamp); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } bool A2DP_BuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf, uint16_t frames_per_packet) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_BuildCodecHeaderSbc(p_codec_info, p_buf, frames_per_packet); case A2DP_MEDIA_CT_AAC: return A2DP_BuildCodecHeaderAac(p_codec_info, p_buf, frames_per_packet); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorBuildCodecHeader(p_codec_info, p_buf, frames_per_packet); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } const tA2DP_ENCODER_INTERFACE* A2DP_GetEncoderInterface( const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_GetEncoderInterfaceSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_GetEncoderInterfaceAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorGetEncoderInterface(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return NULL; } bool A2DP_AdjustCodec(uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_AdjustCodecSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_AdjustCodecAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorAdjustCodec(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } btav_a2dp_codec_index_t A2DP_SourceCodecIndex(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_VERBOSE(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_SourceCodecIndexSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_SourceCodecIndexAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorSourceCodecIndex(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return BTAV_A2DP_CODEC_INDEX_MAX; } const char* A2DP_CodecIndexStr(btav_a2dp_codec_index_t codec_index) { switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: return A2DP_CodecIndexStrSbc(); case BTAV_A2DP_CODEC_INDEX_SINK_SBC: return A2DP_CodecIndexStrSbcSink(); case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: return A2DP_CodecIndexStrAac(); default: break; } if (codec_index < BTAV_A2DP_CODEC_INDEX_MAX) return A2DP_VendorCodecIndexStr(codec_index); return "UNKNOWN CODEC INDEX"; } bool A2DP_InitCodecConfig(btav_a2dp_codec_index_t codec_index, tAVDT_CFG* p_cfg) { LOG_DEBUG(LOG_TAG, "%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index)); /* Default: no content protection info */ p_cfg->num_protect = 0; p_cfg->protect_info[0] = 0; switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: return A2DP_InitCodecConfigSbc(p_cfg); case BTAV_A2DP_CODEC_INDEX_SINK_SBC: return A2DP_InitCodecConfigSbcSink(p_cfg); case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: return A2DP_InitCodecConfigAac(p_cfg); default: break; } if (codec_index < BTAV_A2DP_CODEC_INDEX_MAX) return A2DP_VendorInitCodecConfig(codec_index, p_cfg); return false; } bool check_mm_supports_offload_codec (std::vector& offload_enabled_codecs_config, btav_a2dp_codec_index_t codecIndex) { for (auto offload_codec_config: offload_enabled_codecs_config) { if (codecIndex == offload_codec_config.codec_type) return true; } return false; } bool A2DP_IsHAL2Supported () { #if (OFF_TARGET_TEST_ENABLED == FALSE) return property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", true); #else return false; #endif } void A2DP_SetOffloadStatus(bool offload_status, const char *offload_cap, bool scrambling_support, bool is44p1kFreq_support, std::vector& offload_enabled_codecs_config) { char *tok = NULL; char *tmp_token = NULL; uint8_t add_on_features_size = 0; const bt_device_soc_add_on_features_t *add_on_features_list = controller_get_interface()->get_soc_add_on_features(&add_on_features_size); char vbr_value[PROPERTY_VALUE_MAX] = {'\0'}; char adaptive_value[PROPERTY_VALUE_MAX] = {'\0'}; property_get("persist.vendor.qcom.bluetooth.aac_vbr_ctl.enabled", vbr_value, "false"); if (!(strcmp(vbr_value,"true"))) { BTIF_TRACE_DEBUG("%s: AAC VBR is enabled", __func__); vbr_supported = true; } property_get("persist.vendor.qcom.bluetooth.aptxadaptiver2_1_support", adaptive_value, "false"); if (!(strcmp(adaptive_value,"true"))) { BTIF_TRACE_DEBUG("%s: aptx-adaptive R2.1 is supported", __func__); aptxadaptiver2_1_supported = true; } if (add_on_features_size != 0 && HCI_SPLIT_A2DP_SOURCE_Tx_Split_APTX_ADAPTIVE_SUPPORTED(add_on_features_list->as_array)) { a2dp_aptxadaptive_src_split_tx_supported = true; BTIF_TRACE_DEBUG("%s: split TX is supported", __func__); property_get("persist.vendor.qcom.bluetooth.aptxadaptiver2_2_support", adaptive_value, "false"); if (!(strcmp(adaptive_value,"true"))) { BTIF_TRACE_DEBUG("%s: aptx-adaptive R2.2 is supported", __func__); aptxadaptiver2_2_supported = true; } } LOG_INFO(LOG_TAG,"A2dp_SetOffloadStatus:status = %d", offload_status); mA2dp_offload_status = offload_status; offload_capability = true; if (strcmp(offload_cap,"null") == 0) offload_capability = false; if(add_on_features_size == 0) { BTIF_TRACE_WARNING( "BT controller doesn't have add on features"); } if (A2DP_IsHAL2Supported()) {// 2.0 hybrid offload_capability = offload_status; for (int i = BTAV_A2DP_CODEC_INDEX_MIN; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) { btav_a2dp_codec_index_t codec_index = static_cast(i); //TODO to check for SOC capability switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_SBC_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec(offload_enabled_codecs_config, codec_index )) { sbc_offload = true; sbc_sw = false; } else { sbc_sw = true; sbc_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_AAC_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { aac_offload = true; aac_sw = false; } else { aac_sw = true; aac_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { aptx_offload = true; aptx_sw = false; } else { aptx_sw = true; aptx_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX_HD_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { aptxhd_offload = true; aptxhd_sw = false; } else { // TODO to check for SOC capability aptxhd_sw = true; aptxhd_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX__ADAPTIVE_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { aptx_adaptive_offload = true; aptx_adaptive_sw = false; } else { // TODO to check for SOC capability aptx_adaptive_sw = true; aptx_adaptive_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_LDAC_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { ldac_offload = true; ldac_sw = false; } else { // TODO to check for SOC capability ldac_sw = true; ldac_offload = false; } break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_TWS: if(((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX__TWS_PLUS_SUPPORTED( add_on_features_list->as_array)) && check_mm_supports_offload_codec (offload_enabled_codecs_config, codec_index )) { aptxtws_offload = true; aptxtws_sw = false; } else { aptxtws_sw = true; aptxtws_offload = false; } break; case BTAV_A2DP_QVA_CODEC_INDEX_SOURCE_MAX: case BTAV_A2DP_CODEC_INDEX_SINK_MAX: default: break; } } } else if (mA2dp_offload_status) { // 1.0 HW tok = strtok_r((char*)offload_cap, "-", &tmp_token); while (tok != NULL) { if (strcmp(tok,"sbc") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_SBC_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: SBC offload supported",__func__); sbc_offload = true; } else if (strcmp(tok,"aptx") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: aptX offload supported",__func__); aptx_offload = true; } else if (strcmp(tok,"aac") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_AAC_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: AAC offload supported",__func__); aac_offload = TRUE; } else if (strcmp(tok,"aptxhd") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX_HD_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: APTXHD offload supported",__func__); aptxhd_offload = TRUE; } else if (strcmp(tok,"aptxadaptive") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX__ADAPTIVE_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: APTX Adaptive offload supported",__func__); aptx_adaptive_offload = TRUE; } else if (strcmp(tok,"ldac") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_LDAC_SUPPORTED(add_on_features_list->as_array))) { LOG_INFO(LOG_TAG,"%s: ldac offload supported",__func__); ldac_offload = TRUE; } else if (strcmp(tok,"aptxtws") == 0 && ((add_on_features_size == 0) || HCI_SPLIT_A2DP_SOURCE_APTX__TWS_PLUS_SUPPORTED(add_on_features_list->as_array))) { #if (TWS_ENABLED == TRUE) LOG_INFO(LOG_TAG,"%s: APTXTWS offload supported",__func__); aptxtws_offload = TRUE; #else LOG_INFO(LOG_TAG,"%s: APTXTWS is not supported in host",__func__); aptxtws_offload = FALSE; #endif } tok = strtok_r(NULL, "-", &tmp_token); }; } else { // 1.0 SW sbc_sw = true; aac_sw = true; aptx_sw = true; aptxhd_sw = true; ldac_sw = true; } mA2dp_offload_scrambling_support = scrambling_support; mA2dp_offload_44p1kFreq_support = is44p1kFreq_support; #if (OFF_TARGET_TEST_ENABLED == FALSE) offload_caps = controller_get_interface()->get_a2dp_offload_cap(); #endif } bool A2DP_Get_Aptx_AdaptiveR2_1_Supported() { return aptxadaptiver2_1_supported; } bool A2DP_Get_Aptx_AdaptiveR2_2_Supported() { return aptxadaptiver2_2_supported; } bool A2DP_Get_Source_Aptx_Adaptive_SplitTx_Supported() { return a2dp_aptxadaptive_src_split_tx_supported; } bool A2DP_Get_AAC_VBR_Status(const RawAddress *remote_bdaddr) { if (vbr_supported) { if (interop_match_addr_or_name(INTEROP_DISABLE_AAC_VBR_CODEC, remote_bdaddr)) { APPL_TRACE_DEBUG("AAC VBR is not supported for this BL remote device"); return false; } } return vbr_supported; } bool A2DP_GetOffloadStatus() { return mA2dp_offload_status; } bool A2DP_IsScramblingSupported() { return mA2dp_offload_scrambling_support; } bool A2DP_Is44p1kFreqSupported() { return mA2dp_offload_44p1kFreq_support; } bool A2DP_IsCodecEnabled(btav_a2dp_codec_index_t codec_index) { return (A2DP_IsCodecEnabledInOffload(codec_index) || A2DP_IsCodecEnabledInSoftware(codec_index)); } bool A2DP_IsCodecEnabledInSoftware(btav_a2dp_codec_index_t codec_index) { bool codec_status = false; switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: codec_status = sbc_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: codec_status = aac_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: codec_status = aptx_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: codec_status = aptxhd_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE: codec_status = aptx_adaptive_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: codec_status = ldac_sw; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_TWS: codec_status = aptxtws_sw; break; case BTAV_A2DP_QVA_CODEC_INDEX_SOURCE_MAX: case BTAV_A2DP_CODEC_INDEX_SINK_MAX: default: break; } return codec_status; } bool A2DP_IsCodecEnabledInOffload(btav_a2dp_codec_index_t codec_index) { bool codec_status = false; if (offload_capability) { switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: codec_status = sbc_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: codec_status = aac_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: codec_status = aptx_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: codec_status = aptxhd_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_ADAPTIVE: codec_status = aptx_adaptive_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: if (!ldac_offload) LOG_INFO(LOG_TAG,"LDAC not enabled in offload currently"); codec_status = ldac_offload; break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_TWS: codec_status = aptxtws_offload; break; case BTAV_A2DP_QVA_CODEC_INDEX_SOURCE_MAX: case BTAV_A2DP_CODEC_INDEX_SINK_MAX: default: break; } } return codec_status; } bool A2DP_DumpCodecInfo(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_DEBUG(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_DumpCodecInfoSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_DumpCodecInfoAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorDumpCodecInfo(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return false; } tA2DP_STATUS A2dp_IsCodecConfigMatch(const uint8_t* p_codec_info) { tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info); LOG_DEBUG(LOG_TAG, "%s: codec_type = 0x%x", __func__, codec_type); switch (codec_type) { case A2DP_MEDIA_CT_SBC: return A2DP_IsCodecConfigMatchSbc(p_codec_info); case A2DP_MEDIA_CT_AAC: return A2DP_IsCodecConfigMatchAac(p_codec_info); case A2DP_MEDIA_CT_NON_A2DP: return A2DP_VendorIsCodecConfigMatch(p_codec_info); default: break; } LOG_ERROR(LOG_TAG, "%s: unsupported codec type 0x%x", __func__, codec_type); return A2DP_SUCCESS; } uint8_t A2dp_SendSetConfigRspErrorCodeForPTS() { LOG_DEBUG(LOG_TAG, "%s:", __func__); char is_a2dp_pts_enable[PROPERTY_VALUE_MAX] = "false"; char value[PROPERTY_VALUE_MAX] = {'\0'}; uint8_t error_code = 0; property_get("persist.vendor.bt.a2dp.pts_enable", is_a2dp_pts_enable, "false"); APPL_TRACE_DEBUG("%s: is_a2dp_pts_enable: %s", __func__, is_a2dp_pts_enable); property_get("persist.vendor.bt.a2dp.set_config_error_code", value, "0"); int res = sscanf(value, "%hhu", &error_code); APPL_TRACE_DEBUG("%s: res: %d", __func__, res); APPL_TRACE_DEBUG("%s: error_code: %d", __func__, error_code); if (!strncmp("true", is_a2dp_pts_enable, 4) && (res == 1) && (error_code != 0)) { APPL_TRACE_DEBUG("%s: error_code : %d", __func__, error_code); return error_code; } return error_code; } void print_codec_config(uint8_t codec_arry[]) { for(int i = 0; i < AVDT_CODEC_SIZE; i++) { LOG_INFO(LOG_TAG, "%s: codec_arry[%d] = %d", __func__, i, codec_arry[i]); } } void print_codec_parameters(btav_a2dp_codec_config_t config) { LOG_DEBUG( LOG_TAG, "codec_type=%d codec_priority=%d " "sample_rate=0x%x bits_per_sample=0x%x " "channel_mode=0x%x codec_specific_1=%" PRIi64 " " "codec_specific_2=%" PRIi64 " " "codec_specific_3=%" PRIi64 " " "codec_specific_4=%" PRIi64, config.codec_type, config.codec_priority, config.sample_rate, config.bits_per_sample, config.channel_mode, config.codec_specific_1, config.codec_specific_2, config.codec_specific_3, config.codec_specific_4); }