Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3900181d authored by Bao Do's avatar Bao Do
Browse files

Add support and detection for asymmetrical ASE configurations.

Detect asymmetric configuration in the set configuration and
codec capabilities files and set approriate flag.

Bug: 358194849
Test: atest BluetoothLeAudioCodecsProviderTest
Change-Id: I8103ae402aa2d1d07c2b09717e94d7a8ac384d1f
parent 63b0ff50
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -90,15 +90,15 @@ cc_library_shared {

cc_test {
    name: "BluetoothLeAudioCodecsProviderTest",
    srcs: [
        "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
        "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
    ],
    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/BluetoothLeAudioCodecsProvider.cpp",
        "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
    ],
    header_libs: [
        "libxsdc-utils",
    ],
+46 −9
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@
#include <aidl/android/hardware/bluetooth/audio/Phy.h>
#include <android-base/logging.h>

#include <optional>

#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"

@@ -561,6 +563,17 @@ void AudioSetConfigurationProviderJson::processSubconfig(
  if (ase_cnt == 2) directionAseConfiguration.push_back(config);
}

// Comparing if 2 AseDirectionConfiguration is equal.
// Configuration are copied in, so we can remove some fields for comparison
// without affecting the result.
bool isAseConfigurationEqual(AseDirectionConfiguration cfg_a,
                             AseDirectionConfiguration cfg_b) {
  // Remove unneeded fields when comparing.
  cfg_a.aseConfiguration.metadata = std::nullopt;
  cfg_b.aseConfiguration.metadata = std::nullopt;
  return cfg_a == cfg_b;
}

void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
    const le_audio::AudioSetConfiguration* flat_cfg,
    std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
@@ -569,7 +582,7 @@ void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
    std::vector<std::optional<AseDirectionConfiguration>>&
        sourceAseConfiguration,
    std::vector<std::optional<AseDirectionConfiguration>>& sinkAseConfiguration,
    ConfigurationFlags& /*configurationFlags*/) {
    ConfigurationFlags& configurationFlags) {
  if (flat_cfg == nullptr) {
    LOG(ERROR) << "flat_cfg cannot be null";
    return;
@@ -636,17 +649,41 @@ void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
                         sourceAseConfiguration, location);
      }
    }

    // After putting all subconfig, check if it's an asymmetric configuration
    // and populate information for ConfigurationFlags
    if (!sinkAseConfiguration.empty() && !sourceAseConfiguration.empty()) {
      if (sinkAseConfiguration.size() == sourceAseConfiguration.size()) {
        for (int i = 0; i < sinkAseConfiguration.size(); ++i) {
          if (sinkAseConfiguration[i].has_value() !=
              sourceAseConfiguration[i].has_value()) {
            // Different configuration: one is not empty and other is.
            configurationFlags.bitmask |=
                ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
          } else if (sinkAseConfiguration[i].has_value()) {
            // Both is not empty, comparing inner fields:
            if (!isAseConfigurationEqual(sinkAseConfiguration[i].value(),
                                         sourceAseConfiguration[i].value())) {
              configurationFlags.bitmask |=
                  ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
            }
          }
        }
      } else {
        // Different number of ASE, is a different configuration.
        configurationFlags.bitmask |=
            ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;
      }
    } else {
      if (codec_cfg == nullptr) {
      LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str()
                 << " found";
        LOG(ERROR) << "No codec config matching key "
                   << codec_config_key.c_str() << " found";
      } else {
        LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
                   << "' has no valid subconfigurations.";
      }
    }

  // TODO: Populate information for ConfigurationFlags
  }
}

bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles(
+45 −0
Original line number Diff line number Diff line
@@ -14,10 +14,13 @@
 * limitations under the License.
 */

#include <optional>
#include <set>

#include "aidl/android/hardware/bluetooth/audio/ChannelMode.h"
#include "aidl/android/hardware/bluetooth/audio/CodecId.h"
#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
#include "aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h"
#include "aidl_android_hardware_bluetooth_audio_setting_enums.h"
#define LOG_TAG "BTAudioCodecsProviderAidl"

@@ -52,6 +55,26 @@ BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
  return le_audio_offload_setting;
}

void add_flag(CodecInfo& codec_info, int32_t bitmask) {
  auto& transport =
      codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
  if (!transport.flags.has_value()) transport.flags = ConfigurationFlags();
  transport.flags->bitmask |= bitmask;
}

// Compare 2 codec info to see if they are equal.
// Currently only compare bitdepth, frameDurationUs and samplingFrequencyHz
bool is_equal(CodecInfo& codec_info_a, CodecInfo& codec_info_b) {
  auto& transport_a =
      codec_info_a.transport.get<CodecInfo::Transport::Tag::leAudio>();
  auto& transport_b =
      codec_info_b.transport.get<CodecInfo::Transport::Tag::leAudio>();
  return codec_info_a.name == codec_info_b.name &&
         transport_a.bitdepth == transport_b.bitdepth &&
         transport_a.frameDurationUs == transport_b.frameDurationUs &&
         transport_a.samplingFrequencyHz == transport_b.samplingFrequencyHz;
}

std::unordered_map<SessionType, std::vector<CodecInfo>>
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
    const std::optional<setting::LeAudioOffloadSetting>&
@@ -111,6 +134,9 @@ BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
    codec_info.transport =
        CodecInfo::Transport::make<CodecInfo::Transport::Tag::leAudio>();

    // Add low latency support by default
    add_flag(codec_info, ConfigurationFlags::LOW_LATENCY);

    // Mapping codec configuration information
    auto& transport =
        codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
@@ -152,6 +178,25 @@ BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
    }
  }

  // Goes through a list of scenarios and detect asymmetrical config using
  // codecConfiguration name.
  for (auto& s : supported_scenarios_) {
    if (s.hasEncode() && s.hasDecode() &&
        config_codec_info_map_.count(s.getEncode()) &&
        config_codec_info_map_.count(s.getDecode())) {
      // Check if it's actually using the different codec
      auto& encode_codec_info = config_codec_info_map_[s.getEncode()];
      auto& decode_codec_info = config_codec_info_map_[s.getDecode()];
      if (!is_equal(encode_codec_info, decode_codec_info)) {
        // Change both x and y to become asymmetrical
        add_flag(encode_codec_info,
                 ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS);
        add_flag(decode_codec_info,
                 ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS);
      }
    }
  }

  // Goes through every scenario, deduplicate configuration, skip the invalid
  // config references (e.g. the "invalid" entries in the xml file).
  std::set<std::string> encoding_config, decoding_config, broadcast_config;
+87 −0
Original line number Diff line number Diff line
@@ -20,10 +20,16 @@
#include <tuple>

#include "BluetoothLeAudioCodecsProvider.h"
#include "aidl/android/hardware/bluetooth/audio/CodecInfo.h"
#include "aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h"
#include "aidl/android/hardware/bluetooth/audio/SessionType.h"

using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
using aidl::android::hardware::bluetooth::audio::CodecInfo;
using aidl::android::hardware::bluetooth::audio::ConfigurationFlags;
using aidl::android::hardware::bluetooth::audio::
    LeAudioCodecCapabilitiesSetting;
using aidl::android::hardware::bluetooth::audio::SessionType;
using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
using aidl::android::hardware::bluetooth::audio::setting::
@@ -51,15 +57,30 @@ static const Scenario kValidScenario(std::make_optional("OneChanStereo_16_1"),
static const Scenario kValidBroadcastScenario(
    std::nullopt, std::nullopt, std::make_optional("BcastStereo_16_2"));

static const Scenario kValidAsymmetricScenario(
    std::make_optional("OneChanStereo_32_1"),
    std::make_optional("OneChanStereo_16_1"), std::nullopt);

// Configuration
static const Configuration kValidConfigOneChanStereo_16_1(
    std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
    std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));

static const Configuration kValidConfigOneChanStereo_32_1(
    std::make_optional("OneChanStereo_32_1"), std::make_optional("LC3_32k_1"),
    std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));

// CodecConfiguration
static const CodecConfiguration kValidCodecLC3_16k_1(
    std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
    std::nullopt, std::make_optional(16000), std::make_optional(7500),
    std::make_optional(30), std::nullopt);

static const CodecConfiguration kValidCodecLC3_32k_1(
    std::make_optional("LC3_32k_1"), std::make_optional(CodecType::LC3),
    std::nullopt, std::make_optional(32000), std::make_optional(7500),
    std::make_optional(30), std::nullopt);

// StrategyConfiguration
static const StrategyConfiguration kValidStrategyStereoOneCis(
    std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
@@ -181,6 +202,17 @@ static const std::vector<StrategyConfigurationList>
            kValidStrategyStereoOneCisBoth, kValidStrategyStereoTwoCisBoth,
            kValidStrategyMonoOneCisBoth, kValidStrategyBroadcastStereoBoth})};

// Define some valid asymmetric scenario list
static const std::vector<ScenarioList> kValidAsymmetricScenarioList = {
    ScenarioList(std::vector<Scenario>{kValidAsymmetricScenario})};
static const std::vector<ConfigurationList> kValidAsymmetricConfigurationList =
    {ConfigurationList(std::vector<Configuration>{
        kValidConfigOneChanStereo_16_1, kValidConfigOneChanStereo_32_1})};
static const std::vector<CodecConfigurationList>
    kValidAsymmetricCodecConfigurationList = {
        CodecConfigurationList(std::vector<CodecConfiguration>{
            kValidCodecLC3_16k_1, kValidCodecLC3_32k_1})};

class BluetoothLeAudioCodecsProviderTest
    : public ::testing::TestWithParam<OffloadSetting> {
 public:
@@ -227,6 +259,19 @@ class BluetoothLeAudioCodecsProviderTest
    return le_audio_codec_capabilities;
  }

  std::unordered_map<SessionType, std::vector<CodecInfo>>
  RunCodecInfoTestCase() {
    auto& [scenario_lists, configuration_lists, codec_configuration_lists,
           strategy_configuration_lists] = GetParam();
    LeAudioOffloadSetting le_audio_offload_setting(
        scenario_lists, configuration_lists, codec_configuration_lists,
        strategy_configuration_lists);
    auto le_audio_codec_capabilities =
        BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
            std::make_optional(le_audio_offload_setting));
    return le_audio_codec_capabilities;
  }

 private:
  static inline OffloadSetting CreateTestCase(
      const ScenarioList& scenario_list,
@@ -392,6 +437,39 @@ TEST_P(ComposeLeAudioCodecCapabilitiesTest, CodecCapabilitiesNotEmpty) {
  ASSERT_TRUE(!le_audio_codec_capabilities.empty());
}

class ComposeLeAudioAymmetricCodecInfoTest
    : public BluetoothLeAudioCodecsProviderTest {
 public:
};

TEST_P(ComposeLeAudioAymmetricCodecInfoTest, AsymmetricCodecInfoNotEmpty) {
  Initialize();
  auto le_audio_codec_info_map = RunCodecInfoTestCase();
  ASSERT_TRUE(!le_audio_codec_info_map.empty());
  // Check true asymmetric codec info
  ASSERT_TRUE(!le_audio_codec_info_map
                   [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH]
                       .empty());
  ASSERT_TRUE(!le_audio_codec_info_map
                   [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH]
                       .empty());
  auto required_flag = ConfigurationFlags();
  required_flag.bitmask |= ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS;

  auto codec_info = le_audio_codec_info_map
      [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH][0];
  ASSERT_EQ(codec_info.transport.getTag(), CodecInfo::Transport::Tag::leAudio);
  auto& transport =
      codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
  ASSERT_EQ(transport.flags, std::make_optional(required_flag));

  codec_info = le_audio_codec_info_map
      [SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH][0];
  ASSERT_EQ(codec_info.transport.getTag(), CodecInfo::Transport::Tag::leAudio);
  transport = codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
  ASSERT_EQ(transport.flags, std::make_optional(required_flag));
}

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
INSTANTIATE_TEST_SUITE_P(
    BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
@@ -434,6 +512,15 @@ INSTANTIATE_TEST_SUITE_P(
        kValidScenarioList, kValidConfigurationList,
        kValidCodecConfigurationList, kValidStrategyConfigurationList)));

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    ComposeLeAudioAymmetricCodecInfoTest);
INSTANTIATE_TEST_SUITE_P(
    BluetoothLeAudioCodecsProviderTest, ComposeLeAudioAymmetricCodecInfoTest,
    ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
        kValidAsymmetricScenarioList, kValidAsymmetricConfigurationList,
        kValidAsymmetricCodecConfigurationList,
        kValidStrategyConfigurationList)));

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();