From e062de7b2dd35b8562e7c387be7acfc80487a717 Mon Sep 17 00:00:00 2001 From: Bao Do Date: Tue, 20 Feb 2024 15:45:24 +0800 Subject: [PATCH] Implement HFP codec provider and test Bug: 322280104 Test: atest BluetoothHfpCodecsProviderTest Change-Id: I4c5ca601de61d86a3caae88c47697a2586f4dc5c --- bluetooth/audio/utils/Android.bp | 31 +++- .../BluetoothHfpCodecsProvider.cpp | 81 +++++++++- .../aidl_session/BluetoothHfpCodecsProvider.h | 1 + .../BluetoothHfpCodecsProviderTest.cpp | 153 ++++++++++++++++++ 4 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProviderTest.cpp diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp index 0899441c4e..cecf8f00dd 100644 --- a/bluetooth/audio/utils/Android.bp +++ b/bluetooth/audio/utils/Android.bp @@ -87,8 +87,6 @@ cc_library_shared { ], } -// TODO: Write test for BluetoothHfpCodecsProvider.cpp - cc_test { name: "BluetoothLeAudioCodecsProviderTest", srcs: [ @@ -114,6 +112,35 @@ cc_test { generated_headers: ["le_audio_codec_capabilities"], } +cc_test { + name: "BluetoothHfpCodecsProviderTest", + defaults: [ + "latest_android_hardware_audio_common_ndk_static", + "latest_android_hardware_bluetooth_audio_ndk_static", + "latest_android_media_audio_common_types_ndk_static", + ], + srcs: [ + "aidl_session/BluetoothHfpCodecsProvider.cpp", + "aidl_session/BluetoothHfpCodecsProviderTest.cpp", + ], + header_libs: [ + "libxsdc-utils", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libxml2", + ], + test_suites: [ + "general-tests", + ], + test_options: { + unit_test: false, + }, + generated_sources: ["hfp_codec_capabilities"], + generated_headers: ["hfp_codec_capabilities"], +} + xsd_config { name: "le_audio_codec_capabilities", srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"], diff --git a/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.cpp index be08a39a74..d61ec5a44b 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.cpp @@ -16,21 +16,96 @@ #include "BluetoothHfpCodecsProvider.h" +#include + namespace aidl { namespace android { namespace hardware { namespace bluetooth { namespace audio { +using hfp::setting::CodecType; +using hfp::setting::PathConfiguration; + +static const char* kHfpCodecCapabilitiesFile = + "/vendor/etc/aidl/hfp/hfp_codec_capabilities.xml"; + std::optional BluetoothHfpCodecsProvider::ParseFromHfpOffloadSettingFile() { - return std::nullopt; + auto hfp_offload_setting = + hfp::setting::readHfpOffloadSetting(kHfpCodecCapabilitiesFile); + if (!hfp_offload_setting.has_value()) { + LOG(ERROR) << __func__ << ": Failed to read " << kHfpCodecCapabilitiesFile; + } + return hfp_offload_setting; } std::vector BluetoothHfpCodecsProvider::GetHfpAudioCodecInfo( const std::optional& hfp_offload_setting) { - (void)hfp_offload_setting; - return std::vector(); + std::vector result; + if (!hfp_offload_setting.has_value()) return result; + + // Convert path configuration into map + // Currently transport configuration is unused + if (!hfp_offload_setting.value().hasPathConfiguration() || + hfp_offload_setting.value().getPathConfiguration().empty()) { + LOG(WARNING) << __func__ << ": path configurations is empty"; + return result; + } + auto path_configurations = hfp_offload_setting.value().getPathConfiguration(); + std::unordered_map path_config_map; + for (const auto& path_cfg : path_configurations) + if (path_cfg.hasName() && path_cfg.hasDataPath()) + path_config_map.insert(make_pair(path_cfg.getName(), path_cfg)); + + for (const auto& cfg : hfp_offload_setting.value().getConfiguration()) { + auto input_path_cfg = path_config_map.find(cfg.getInputPathConfiguration()); + auto output_path_cfg = + path_config_map.find(cfg.getOutputPathConfiguration()); + if (input_path_cfg == path_config_map.end()) { + LOG(WARNING) << __func__ << ": Input path configuration not found: " + << cfg.getInputPathConfiguration(); + continue; + } + + if (output_path_cfg == path_config_map.end()) { + LOG(WARNING) << __func__ << ": Output path configuration not found: " + << cfg.getOutputPathConfiguration(); + continue; + } + + CodecInfo codec_info; + + switch (cfg.getCodec()) { + case CodecType::LC3: + codec_info.id = CodecId::Core::LC3; + break; + case CodecType::MSBC: + codec_info.id = CodecId::Core::MSBC; + break; + case CodecType::CVSD: + codec_info.id = CodecId::Core::CVSD; + break; + default: + LOG(WARNING) << __func__ << ": Unknown codec from " << cfg.getName(); + codec_info.id = CodecId::Vendor(); + break; + } + codec_info.name = cfg.getName(); + + codec_info.transport = + CodecInfo::Transport::make(); + + auto& transport = + codec_info.transport.get(); + transport.useControllerCodec = cfg.getUseControllerCodec(); + transport.inputDataPath = input_path_cfg->second.getDataPath(); + transport.outputDataPath = output_path_cfg->second.getDataPath(); + + result.push_back(codec_info); + } + LOG(INFO) << __func__ << ": Has " << result.size() << " codec info"; + return result; } } // namespace audio diff --git a/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.h index d2196a2459..642ee02821 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.h +++ b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProvider.h @@ -22,6 +22,7 @@ #include "aidl/android/hardware/bluetooth/audio/CodecInfo.h" #include "aidl_android_hardware_bluetooth_audio_hfp_setting.h" +#include "aidl_android_hardware_bluetooth_audio_hfp_setting_enums.h" namespace aidl { namespace android { diff --git a/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProviderTest.cpp b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProviderTest.cpp new file mode 100644 index 0000000000..b08c3ebaf7 --- /dev/null +++ b/bluetooth/audio/utils/aidl_session/BluetoothHfpCodecsProviderTest.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2024 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. + */ + +#include + +#include +#include + +#include "BluetoothHfpCodecsProvider.h" +#include "gtest/gtest.h" + +using aidl::android::hardware::bluetooth::audio::BluetoothHfpCodecsProvider; +using aidl::android::hardware::bluetooth::audio::CodecInfo; +using aidl::android::hardware::bluetooth::audio::hfp::setting::CodecType; +using aidl::android::hardware::bluetooth::audio::hfp::setting::Configuration; +using aidl::android::hardware::bluetooth::audio::hfp::setting:: + HfpOffloadSetting; +using aidl::android::hardware::bluetooth::audio::hfp::setting:: + PathConfiguration; +using aidl::android::hardware::bluetooth::audio::hfp::setting:: + TransportConfiguration; + +typedef std::tuple, + std::vector, + std::vector> + HfpOffloadSettingTuple; + +// Define valid components for each list +// PathConfiguration +static const PathConfiguration kValidPathConfigurationCVSD("CVSD_IO", 16000, + CodecType::CVSD, 16, + 2, 0, 1, 0); +static const PathConfiguration kInvalidPathConfigurationNULL(std::nullopt, + 16000, + CodecType::CVSD, + 16, 2, 0, 1, 0); +static const PathConfiguration kInvalidPathConfigurationNoPath( + "CVSD_NULL", 16000, CodecType::CVSD, 16, 2, 0, std::nullopt, 0); + +// Configuration +static const Configuration kValidConfigurationCVSD("CVSD", CodecType::CVSD, + 65535, 7, 0, true, "CVSD_IO", + "CVSD_IO", std::nullopt, + std::nullopt); +static const Configuration kInvalidConfigurationCVSDNoPath( + "CVSD", CodecType::CVSD, 65535, 7, 0, true, "CVSD_NULL", "CVSD_NULL", + std::nullopt, std::nullopt); +static const Configuration kInvalidConfigurationCVSDNotFound( + "CVSD", CodecType::CVSD, 65535, 7, 0, true, "CVSD_N", "CVSD_N", + std::nullopt, std::nullopt); + +class BluetoothHfpCodecsProviderTest : public ::testing::Test { + public: + static std::vector CreateTestCases( + const std::vector> path_configs_list, + const std::vector> + transport_configs_list, + const std::vector> configs_list) { + std::vector test_cases; + for (const auto& path_configs : path_configs_list) { + for (const auto& transport_configs : transport_configs_list) { + for (const auto& configs : configs_list) + test_cases.push_back( + CreateTestCase(path_configs, transport_configs, configs)); + } + } + return test_cases; + } + + protected: + std::vector RunTestCase(HfpOffloadSettingTuple test_case) { + auto& [path_configuration_list, transport_configuration_list, + configuration_list] = test_case; + HfpOffloadSetting hfp_offload_setting(path_configuration_list, + transport_configuration_list, + configuration_list); + auto capabilities = + BluetoothHfpCodecsProvider::GetHfpAudioCodecInfo(hfp_offload_setting); + return capabilities; + } + + private: + static inline HfpOffloadSettingTuple CreateTestCase( + const std::vector path_config_list, + const std::vector transport_config_list, + const std::vector config_list) { + return std::make_tuple(path_config_list, transport_config_list, + config_list); + } +}; + +class GetHfpCodecInfoTest : public BluetoothHfpCodecsProviderTest { + public: + static std::vector> + GetInvalidPathConfigurationLists() { + std::vector> result; + result.push_back({kInvalidPathConfigurationNULL}); + result.push_back({kInvalidPathConfigurationNoPath}); + result.push_back({}); + return result; + } + + static std::vector> + GetInvalidConfigurationLists() { + std::vector> result; + result.push_back({kInvalidConfigurationCVSDNotFound}); + result.push_back({kInvalidConfigurationCVSDNoPath}); + result.push_back({}); + return result; + } +}; + +TEST_F(GetHfpCodecInfoTest, InvalidPathConfiguration) { + auto test_cases = BluetoothHfpCodecsProviderTest::CreateTestCases( + GetHfpCodecInfoTest::GetInvalidPathConfigurationLists(), {{}}, + {{kValidConfigurationCVSD}}); + for (auto& test_case : test_cases) { + auto hfp_codec_capabilities = RunTestCase(test_case); + ASSERT_TRUE(hfp_codec_capabilities.empty()); + } +} + +TEST_F(GetHfpCodecInfoTest, InvalidConfigurationName) { + auto test_cases = BluetoothHfpCodecsProviderTest::CreateTestCases( + GetHfpCodecInfoTest::GetInvalidPathConfigurationLists(), {{}}, + {GetHfpCodecInfoTest::GetInvalidConfigurationLists()}); + for (auto& test_case : test_cases) { + auto hfp_codec_capabilities = RunTestCase(test_case); + ASSERT_TRUE(hfp_codec_capabilities.empty()); + } +} + +TEST_F(GetHfpCodecInfoTest, ValidConfiguration) { + auto test_cases = BluetoothHfpCodecsProviderTest::CreateTestCases( + {{kValidPathConfigurationCVSD}}, {{}}, {{kValidConfigurationCVSD}}); + for (auto& test_case : test_cases) { + auto hfp_codec_capabilities = RunTestCase(test_case); + ASSERT_FALSE(hfp_codec_capabilities.empty()); + } +}