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

Commit d30b94bd authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Gerrit Code Review
Browse files

Merge changes Ieec55300,Ie2b9d1a8,Ia65e4804 into main

* changes:
  LeAudio: Support vendor configurations in control point operations
  LeAudio: Allow for vendor codec specific capabilities
  LeAudio: Improve LTV map structure
parents f532b9a6 767f1d65
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -866,6 +866,7 @@ cc_test {
        "le_audio/le_audio_set_configuration_provider_json.cc",
        "le_audio/le_audio_types.cc",
        "le_audio/le_audio_types_test.cc",
        "le_audio/le_audio_utils.cc",
        "le_audio/metrics_collector_linux.cc",
        "le_audio/mock_codec_interface.cc",
        "le_audio/mock_codec_manager.cc",
@@ -1059,6 +1060,7 @@ cc_test {
        "le_audio/broadcaster/state_machine.cc",
        "le_audio/broadcaster/state_machine_test.cc",
        "le_audio/le_audio_types.cc",
        "le_audio/le_audio_utils.cc",
        "le_audio/mock_codec_interface.cc",
        "le_audio/mock_codec_manager.cc",
        "le_audio/mock_iso_manager.cc",
+35 −20
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@

#include "internal_include/bt_trace.h"
#include "le_audio_types.h"
#include "le_audio_utils.h"
#include "os/log.h"
#include "stack/include/bt_types.h"

@@ -314,22 +315,33 @@ bool PrepareAseCtpCodecConfig(const std::vector<struct ctp_codec_conf>& confs,
                              std::vector<uint8_t>& value) {
  if (confs.size() == 0) return false;

  std::string conf_ents_str;
  std::stringstream conf_ents_str;
  size_t msg_len = std::accumulate(
      confs.begin(), confs.end(),
      confs.size() * kCtpCodecConfMinLen + kAseNumSize + kCtpOpSize,
      [&conf_ents_str](size_t cur_len, auto const& conf) {
        for (const auto& [type, value] : conf.codec_config.Values()) {
          conf_ents_str +=
              "\ttype: " + std::to_string(type) +
              "\tlen: " + std::to_string(value.size()) +
              "\tdata: " + base::HexEncode(value.data(), value.size()) + "\n";
        };
        if (utils::IsCodecUsingLtvFormat(conf.codec_id)) {
          types::LeAudioLtvMap ltv;
          if (ltv.Parse(conf.codec_config.data(), conf.codec_config.size())) {
            for (const auto& [type, value] : ltv.Values()) {
              conf_ents_str
                  << "\ttype: " << std::to_string(type)
                  << "\tlen: " << std::to_string(value.size())
                  << "\tdata: " << base::HexEncode(value.data(), value.size())
                  << "\n";
            }
            return cur_len + conf.codec_config.size();
          }
          LOG_ERROR("Error parsing codec configuration LTV data.");
        }

        return cur_len + conf.codec_config.RawPacketSize();
        conf_ents_str << "\t"
                      << base::HexEncode(conf.codec_config.data(),
                                         conf.codec_config.size());
        return cur_len + conf.codec_config.size();
      });
  value.resize(msg_len);

  value.resize(msg_len);
  uint8_t* msg = value.data();
  UINT8_TO_STREAM(msg, kCtpOpcodeCodecConfiguration);

@@ -342,10 +354,9 @@ bool PrepareAseCtpCodecConfig(const std::vector<struct ctp_codec_conf>& confs,
    UINT16_TO_STREAM(msg, conf.codec_id.vendor_company_id);
    UINT16_TO_STREAM(msg, conf.codec_id.vendor_codec_id);

    auto codec_spec_conf_len = conf.codec_config.RawPacketSize();

    UINT8_TO_STREAM(msg, codec_spec_conf_len);
    msg = conf.codec_config.RawPacket(msg);
    UINT8_TO_STREAM(msg, conf.codec_config.size());
    ARRAY_TO_STREAM(msg, conf.codec_config.data(),
                    static_cast<int>(conf.codec_config.size()));

    LOG(INFO) << __func__ << ", Codec configuration"
              << "\n\tAse id: " << loghex(conf.ase_id)
@@ -357,10 +368,10 @@ bool PrepareAseCtpCodecConfig(const std::vector<struct ctp_codec_conf>& confs,
              << "\n\tVendor codec ID: "
              << loghex(conf.codec_id.vendor_codec_id)
              << "\n\tCodec config len: "
              << static_cast<int>(codec_spec_conf_len)
              << static_cast<int>(conf.codec_config.size())
              << "\n\tCodec spec conf: "
              << "\n"
              << conf_ents_str;
              << conf_ents_str.str();
  }

  return true;
@@ -593,10 +604,14 @@ int ParseSinglePac(std::vector<struct acs_ac_record>& pac_recs, uint16_t len,
    return -1;
  }

  rec.codec_spec_caps_raw.assign(value, value + codec_spec_cap_len);

  if (utils::IsCodecUsingLtvFormat(rec.codec_id)) {
    bool parsed;
    rec.codec_spec_caps =
        types::LeAudioLtvMap::Parse(value, codec_spec_cap_len, parsed);
    if (!parsed) return -1;
  }

  value += codec_spec_cap_len;
  len -= codec_spec_cap_len;
+1 −1
Original line number Diff line number Diff line
@@ -163,7 +163,7 @@ struct ctp_codec_conf {
  uint8_t target_latency;
  uint8_t target_phy;
  types::LeAudioCodecId codec_id;
  types::LeAudioLtvMap codec_config;
  std::vector<uint8_t> codec_config;
};

constexpr uint16_t kCtpQosConfMinLen = 16;
+97 −46
Original line number Diff line number Diff line
@@ -153,11 +153,11 @@ TEST(LeAudioClientParserTest, testParsePacsInvalidCapsLtvLen) {
      // Num records
      0x01,
      // Codec_ID
      0x01,
      0x03,
      0x02,
      0x05,
      0x04,
      0x06,
      0x00,
      0x00,
      0x00,
      0x00,
      // Codec Spec. Caps. Len
      0x07,
      // Codec Spec. Caps.
@@ -177,11 +177,11 @@ TEST(LeAudioClientParserTest, testParsePacsInvalidCapsLtvLen) {
      // Num records
      0x01,
      // Codec_ID
      0x01,
      0x03,
      0x02,
      0x05,
      0x04,
      0x06,
      0x00,
      0x00,
      0x00,
      0x00,
      // Codec Spec. Caps. Len
      0x07,
      // Codec Spec. Caps.
@@ -205,11 +205,11 @@ TEST(LeAudioClientParserTest, testParsePacsNullLtv) {
      // Num records
      0x01,
      // Codec_ID
      0x01,
      0x03,
      0x02,
      0x05,
      0x04,
      0x06,
      0x00,
      0x00,
      0x00,
      0x00,
      // Codec Spec. Caps. Len
      0x0A,
      // Codec Spec. Caps.
@@ -229,9 +229,9 @@ TEST(LeAudioClientParserTest, testParsePacsNullLtv) {
  ASSERT_TRUE(ParsePacs(pac_recs, sizeof(value), value));

  ASSERT_EQ(pac_recs.size(), 1u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x01u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0203u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0405u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x06u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0000u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0000u);

  auto codec_spec_caps = pac_recs[0].codec_spec_caps.Values();
  ASSERT_EQ(codec_spec_caps.size(), 3u);
@@ -244,6 +244,12 @@ TEST(LeAudioClientParserTest, testParsePacsNullLtv) {
  ASSERT_EQ(codec_spec_caps[0x03u][1], 0x05u);
  ASSERT_EQ(codec_spec_caps.count(0x04u), 1u);
  ASSERT_EQ(codec_spec_caps[0x04u].size(), 0u);

  // Validate the raw data from ltv matches the original pac record data buffer
  // Add one redundant ltv length of 0, just like in the value[] above.
  auto ltv_raw = pac_recs[0].codec_spec_caps.RawPacket();
  ltv_raw.push_back(0x00);
  ASSERT_EQ(ltv_raw, pac_recs[0].codec_spec_caps_raw);
}

TEST(LeAudioClientParserTest, testParsePacsEmptyMeta) {
@@ -253,11 +259,11 @@ TEST(LeAudioClientParserTest, testParsePacsEmptyMeta) {
      // Num records
      0x01,
      // Codec_ID
      0x01,
      0x03,
      0x02,
      0x05,
      0x04,
      0x06,
      0x00,
      0x00,
      0x00,
      0x00,
      // Codec Spec. Caps. Len
      0x07,
      // Codec Spec. Caps.
@@ -274,9 +280,9 @@ TEST(LeAudioClientParserTest, testParsePacsEmptyMeta) {
  ASSERT_TRUE(ParsePacs(pac_recs, sizeof(value), value));

  ASSERT_EQ(pac_recs.size(), 1u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x01u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0203u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0405u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x06u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0000u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0000u);

  auto codec_spec_caps = pac_recs[0].codec_spec_caps.Values();
  ASSERT_EQ(codec_spec_caps.size(), 2u);
@@ -287,6 +293,10 @@ TEST(LeAudioClientParserTest, testParsePacsEmptyMeta) {
  ASSERT_EQ(codec_spec_caps[0x03u].size(), 2u);
  ASSERT_EQ(codec_spec_caps[0x03u][0], 0x04u);
  ASSERT_EQ(codec_spec_caps[0x03u][1], 0x05u);

  // Validate the raw data from ltv matches the original pac record data buffer
  ASSERT_EQ(pac_recs[0].codec_spec_caps.RawPacket(),
            pac_recs[0].codec_spec_caps_raw);
}

TEST(LeAudioClientParserTest, testParsePacsInvalidMetaLength) {
@@ -325,7 +335,7 @@ TEST(LeAudioClientParserTest, testParsePacsValidMeta) {
      // Num records
      0x01,
      // Codec_ID
      0x01, 0x03, 0x02, 0x05, 0x04,
      0x06, 0x00, 0x00, 0x00, 0x00,
      // Codec Spec. Caps. Len
      0x07,
      // Codec Spec. Caps.
@@ -347,9 +357,9 @@ TEST(LeAudioClientParserTest, testParsePacsValidMeta) {
  ASSERT_TRUE(ParsePacs(pac_recs, sizeof(value), value));

  ASSERT_EQ(pac_recs.size(), 1u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x01u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0203u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0405u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x06u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0000u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0000u);

  auto codec_spec_caps = pac_recs[0].codec_spec_caps.Values();
  ASSERT_EQ(codec_spec_caps.size(), 2u);
@@ -366,6 +376,10 @@ TEST(LeAudioClientParserTest, testParsePacsValidMeta) {
  ASSERT_EQ(pac_recs[0].metadata[1], 0x02u);
  ASSERT_EQ(pac_recs[0].metadata[2], 0x01u);
  ASSERT_EQ(pac_recs[0].metadata[3], 0x00u);

  // Validate the raw data from ltv matches the original pac record data buffer
  ASSERT_EQ(pac_recs[0].codec_spec_caps.RawPacket(),
            pac_recs[0].codec_spec_caps_raw);
}

TEST(LeAudioClientParserTest, testParsePacsInvalidNumRecords) {
@@ -410,7 +424,7 @@ TEST(LeAudioClientParserTest, testParsePacsMultipleRecords) {
      // Metadata Length
      0x00,
      // Codec_ID
      0x06, 0x08, 0x07, 0x0A, 0x09,
      0x06, 0x00, 0x00, 0x00, 0x00,
      // Codec Spec. Caps. Len
      0x03,
      // Codec Spec. Caps.
@@ -453,15 +467,15 @@ TEST(LeAudioClientParserTest, testParsePacsMultipleRecords) {
  ASSERT_EQ(record0.codec_id.coding_format, 0x01u);
  ASSERT_EQ(record0.codec_id.vendor_company_id, 0x0203u);
  ASSERT_EQ(record0.codec_id.vendor_codec_id, 0x0405u);
  ASSERT_EQ(record0.codec_spec_caps.Size(), 0u);
  ASSERT_EQ(record0.codec_spec_caps_raw.size(), 0u);
  ASSERT_EQ(record0.metadata.size(), 0u);

  // Verify 2nd record
  auto& record1 = pac_recs[1];

  ASSERT_EQ(record1.codec_id.coding_format, 0x06u);
  ASSERT_EQ(record1.codec_id.vendor_company_id, 0x0708u);
  ASSERT_EQ(record1.codec_id.vendor_codec_id, 0x090Au);
  ASSERT_EQ(record1.codec_id.vendor_company_id, 0x0000u);
  ASSERT_EQ(record1.codec_id.vendor_codec_id, 0x0000u);

  auto codec_spec_caps1 = record1.codec_spec_caps.Values();
  ASSERT_EQ(codec_spec_caps1.size(), 1u);
@@ -475,6 +489,9 @@ TEST(LeAudioClientParserTest, testParsePacsMultipleRecords) {
  ASSERT_EQ(record1.metadata[2], 0x01u);
  ASSERT_EQ(record1.metadata[3], 0x00u);

  // Validate the raw data from ltv matches the original pac record data buffer
  ASSERT_EQ(record1.codec_spec_caps.RawPacket(), record1.codec_spec_caps_raw);

  // Verify 3rd record
  auto& record2 = pac_recs[2];

@@ -482,15 +499,11 @@ TEST(LeAudioClientParserTest, testParsePacsMultipleRecords) {
  ASSERT_EQ(record2.codec_id.vendor_company_id, 0x1213u);
  ASSERT_EQ(record2.codec_id.vendor_codec_id, 0x1415u);

  auto codec_spec_caps2 = record2.codec_spec_caps.Values();
  ASSERT_EQ(codec_spec_caps2.size(), 2u);
  ASSERT_EQ(codec_spec_caps2.count(0x12u), 1u);
  ASSERT_EQ(codec_spec_caps2[0x12u].size(), 1u);
  ASSERT_EQ(codec_spec_caps2[0x12u][0], 0x13u);
  ASSERT_EQ(codec_spec_caps2.count(0x13u), 1u);
  ASSERT_EQ(codec_spec_caps2[0x13u].size(), 2u);
  ASSERT_EQ(codec_spec_caps2[0x13u][0], 0x14u);
  ASSERT_EQ(codec_spec_caps2[0x13u][1], 0x15u);
  // Codec is not known to use LTV format for codec specific parameters
  ASSERT_EQ(record2.codec_spec_caps_raw.size(), 7u);
  ASSERT_EQ(record2.codec_spec_caps.Size(), 0u);
  ASSERT_EQ(0, memcmp(record2.codec_spec_caps_raw.data(), value + 28,
                      record2.codec_spec_caps_raw.size()));

  ASSERT_EQ(record2.metadata.size(), 4u);
  ASSERT_EQ(record2.metadata[0], 0x03u);
@@ -499,6 +512,44 @@ TEST(LeAudioClientParserTest, testParsePacsMultipleRecords) {
  ASSERT_EQ(record2.metadata[3], 0x10u);
}

TEST(LeAudioClientParserTest, testParsePacsVendorCodecRecords) {
  std::vector<struct types::acs_ac_record> pac_recs;

  const uint8_t value[] = {
      // Num records
      0x01,
      // Vendor Codec_ID
      0x01,
      0x03,
      0x02,
      0x05,
      0x04,
      // Codec Spec. Caps. Len
      0x0A,
      // Codec Spec. Caps. - proprietary format
      0x01,
      0x02,
      0x03,
      0x04,
      0x05,
      0x06,
      0x07,
      0x08,
      0x09,
      0x0A,
      // Metadata Length
      0x00,
  };
  ASSERT_TRUE(ParsePacs(pac_recs, sizeof(value), value));

  ASSERT_EQ(pac_recs.size(), 1u);
  ASSERT_EQ(pac_recs[0].codec_id.coding_format, 0x01u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_company_id, 0x0203u);
  ASSERT_EQ(pac_recs[0].codec_id.vendor_codec_id, 0x0405u);
  ASSERT_TRUE(pac_recs[0].codec_spec_caps.IsEmpty());
  ASSERT_EQ(0x0Au, pac_recs[0].codec_spec_caps_raw.size());
}

TEST(LeAudioClientParserTest, testParseAudioLocationsInvalidLength) {
  types::AudioLocations locations = codec_spec_conf::kLeAudioLocationNotAllowed;
  const uint8_t value1[] = {
@@ -1126,7 +1177,7 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigSingle) {
      .target_latency = 0x03,
      .target_phy = 0x02,
      .codec_id = codec_id,
      .codec_config = codec_conf,
      .codec_config = codec_conf.RawPacket(),
  });
  PrepareAseCtpCodecConfig(confs, value);

@@ -1186,7 +1237,7 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigMultiple) {
      .target_latency = 0x03,
      .target_phy = 0x02,
      .codec_id = codec_id,
      .codec_config = codec_conf,
      .codec_config = codec_conf.RawPacket(),
  });
  PrepareAseCtpCodecConfig(confs, value);

@@ -1241,7 +1292,7 @@ TEST(LeAudioClientParserTest, testPrepareAseCtpCodecConfigMultiple) {
      .target_latency = 0x13,
      .target_phy = 0x01,
      .codec_id = codec_id2,
      .codec_config = codec_conf2,
      .codec_config = codec_conf2.RawPacket(),
  });
  PrepareAseCtpCodecConfig(confs, value);

+29 −9
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "bta_gatt_queue.h"
#include "btif/include/btif_storage.h"
#include "internal_include/bt_trace.h"
#include "le_audio_utils.h"

using bluetooth::hci::kIsoCigPhy1M;
using bluetooth::hci::kIsoCigPhy2M;
@@ -360,16 +361,24 @@ void LeAudioDevice::RegisterPACs(

  /* TODO wrap this logging part with debug flag */
  for (const struct types::acs_ac_record& pac : *pac_recs) {
    LOG(INFO) << "Registering PAC"
    std::stringstream debug_str;
    debug_str << "Registering PAC"
              << "\n\tCoding format: " << loghex(pac.codec_id.coding_format)
              << "\n\tVendor codec company ID: "
              << loghex(pac.codec_id.vendor_company_id)
              << "\n\tVendor codec ID: " << loghex(pac.codec_id.vendor_codec_id)
              << "\n\tCodec spec caps:\n"
              << pac.codec_spec_caps.ToString("",
                                              types::CodecCapabilitiesLtvFormat)
              << "\n\tMetadata: "
              << "\n\tCodec spec caps:\n";
    if (utils::IsCodecUsingLtvFormat(pac.codec_id) &&
        !pac.codec_spec_caps.IsEmpty()) {
      debug_str << pac.codec_spec_caps.ToString(
          "", types::CodecCapabilitiesLtvFormat);
    } else {
      debug_str << base::HexEncode(pac.codec_spec_caps_raw.data(),
                                   pac.codec_spec_caps_raw.size());
    }
    debug_str << "\n\tMetadata: "
              << base::HexEncode(pac.metadata.data(), pac.metadata.size());
    LOG_DEBUG("%s", debug_str.str().c_str());

    if (IS_FLAG_ENABLED(leaudio_dynamic_spatial_audio)) {
      if (pac.codec_id == types::kLeAudioCodecHeadtracking) {
@@ -700,8 +709,13 @@ uint8_t LeAudioDevice::GetSupportedAudioChannelCounts(uint8_t direction) const {
    auto& pac_recs = std::get<1>(pac_tuple);

    for (const auto pac : pac_recs) {
      if (pac.codec_id.coding_format != types::kLeAudioCodingFormatLC3)
      if (!utils::IsCodecUsingLtvFormat(pac.codec_id)) {
        LOG_WARN("Unknown codec PAC record for codec: %s",
                 bluetooth::common::ToString(pac.codec_id).c_str());
        continue;
      }
      ASSERT_LOG(!pac.codec_spec_caps.IsEmpty(),
                 "Codec specific capabilities are not parsed approprietly.");

      auto supported_channel_count_ltv = pac.codec_spec_caps.Find(
          codec_spec_caps::kLeAudioLtvTypeSupportedAudioChannelCounts);
@@ -803,9 +817,15 @@ void LeAudioDevice::DumpPacsDebugState(std::stringstream& stream,
               << static_cast<int>(record.codec_id.vendor_company_id)
               << ", Vendor codec ID: "
               << static_cast<int>(record.codec_id.vendor_codec_id) << ")";
        stream << "\n\t\tCodec specific capabilities:\n"
               << record.codec_spec_caps.ToString(
        stream << "\n\t\tCodec specific capabilities:\n";
        if (utils::IsCodecUsingLtvFormat(record.codec_id)) {
          stream << record.codec_spec_caps.ToString(
              "\t\t\t", types::CodecCapabilitiesLtvFormat);
        } else {
          stream << "\t\t\t"
                 << base::HexEncode(record.codec_spec_caps_raw.data(),
                                    record.codec_spec_caps_raw.size());
        }
        stream << "\t\tMetadata: "
               << base::HexEncode(record.metadata.data(),
                                  record.metadata.size());
Loading