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

Commit 3e16d20c authored by Bao Do's avatar Bao Do Committed by Automerger Merge Worker
Browse files

Add support and detection for asymmetrical ASE configurations. am: 3900181d am: e5982146

parents 79e8e2ac e5982146
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();