Implement HFP codec provider and test

Bug: 322280104
Test: atest BluetoothHfpCodecsProviderTest
Change-Id: I4c5ca601de61d86a3caae88c47697a2586f4dc5c
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index 0899441..cecf8f0 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -87,8 +87,6 @@
     ],
 }
 
-// TODO: Write test for BluetoothHfpCodecsProvider.cpp
-
 cc_test {
     name: "BluetoothLeAudioCodecsProviderTest",
     srcs: [
@@ -114,6 +112,35 @@
     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 be08a39..d61ec5a 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 <unordered_map>
+
 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<HfpOffloadSetting>
 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<CodecInfo> BluetoothHfpCodecsProvider::GetHfpAudioCodecInfo(
     const std::optional<HfpOffloadSetting>& hfp_offload_setting) {
-  (void)hfp_offload_setting;
-  return std::vector<CodecInfo>();
+  std::vector<CodecInfo> 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<std::string, PathConfiguration> 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<CodecInfo::Transport::Tag::hfp>();
+
+    auto& transport =
+        codec_info.transport.get<CodecInfo::Transport::Tag::hfp>();
+    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 d2196a2..642ee02 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 0000000..b08c3eb
--- /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 <gtest/gtest.h>
+
+#include <optional>
+#include <tuple>
+
+#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<PathConfiguration>,
+                   std::vector<TransportConfiguration>,
+                   std::vector<Configuration>>
+    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<HfpOffloadSettingTuple> CreateTestCases(
+      const std::vector<std::vector<PathConfiguration>> path_configs_list,
+      const std::vector<std::vector<TransportConfiguration>>
+          transport_configs_list,
+      const std::vector<std::vector<Configuration>> configs_list) {
+    std::vector<HfpOffloadSettingTuple> 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<CodecInfo> 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<PathConfiguration> path_config_list,
+      const std::vector<TransportConfiguration> transport_config_list,
+      const std::vector<Configuration> config_list) {
+    return std::make_tuple(path_config_list, transport_config_list,
+                           config_list);
+  }
+};
+
+class GetHfpCodecInfoTest : public BluetoothHfpCodecsProviderTest {
+ public:
+  static std::vector<std::vector<PathConfiguration>>
+  GetInvalidPathConfigurationLists() {
+    std::vector<std::vector<PathConfiguration>> result;
+    result.push_back({kInvalidPathConfigurationNULL});
+    result.push_back({kInvalidPathConfigurationNoPath});
+    result.push_back({});
+    return result;
+  }
+
+  static std::vector<std::vector<Configuration>>
+  GetInvalidConfigurationLists() {
+    std::vector<std::vector<Configuration>> 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());
+  }
+}