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

Commit e085b76c authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Allow for vendor codec specific capabilities

Support propertiary codec specific parameters in PAC records.

Bug: 308427707
Test: atest --host bluetooth_le_audio_test bluetooth_le_audio_client_test bluetooth_test_broadcaster bluetooth_test_broadcaster_state_machine bluetooth_le_audio_codec_manager_test
Flag: EXEMPT; refactor for the multicodec support, verified with unit tests
Change-Id: Ie2b9d1a8258a7c84efc77ee847366d9010dc98fc
parent 8145d602
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -846,6 +846,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",
@@ -1037,6 +1038,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",
+9 −4
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"

@@ -593,10 +594,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;
+94 −43
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[] = {
+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());
+28 −24
Original line number Diff line number Diff line
@@ -385,9 +385,7 @@ class PublishedAudioCapabilitiesBuilder {
    uint32_t octets_per_frame_range =
        octets_per_frame | (octets_per_frame << 16);

    pac_records_.push_back(
        acs_ac_record({.codec_id = codec_id,
                       .codec_spec_caps = LeAudioLtvMap({
    auto ltv_map = LeAudioLtvMap({
        {kLeAudioLtvTypeSupportedSamplingFrequencies,
         UINT16_TO_VEC_UINT8(sampling_frequencies)},
        {kLeAudioLtvTypeSupportedFrameDurations,
@@ -398,7 +396,11 @@ class PublishedAudioCapabilitiesBuilder {
         UINT32_TO_VEC_UINT8(octets_per_frame_range)},
        {kLeAudioLtvTypeSupportedMaxCodecFramesPerSdu,
         UINT8_TO_VEC_UINT8(max_codec_frames_per_sdu)},
                       }),
    });
    pac_records_.push_back(
        acs_ac_record({.codec_id = codec_id,
                       .codec_spec_caps = ltv_map,
                       .codec_spec_caps_raw = ltv_map.RawPacket(),
                       .metadata = std::vector<uint8_t>(0)}));
  }

@@ -409,9 +411,7 @@ class PublishedAudioCapabilitiesBuilder {
    uint32_t octets_per_frame_range =
        octets_per_frame_min | (ocets_per_frame_max << 16);

    pac_records_.push_back(
        acs_ac_record({.codec_id = codec_id,
                       .codec_spec_caps = LeAudioLtvMap({
    auto ltv_map = LeAudioLtvMap({
        {kLeAudioLtvTypeSupportedSamplingFrequencies,
         UINT16_TO_VEC_UINT8(capa_sampling_frequency)},
        {kLeAudioLtvTypeSupportedFrameDurations,
@@ -422,7 +422,11 @@ class PublishedAudioCapabilitiesBuilder {
         UINT32_TO_VEC_UINT8(octets_per_frame_range)},
        {kLeAudioLtvTypeSupportedMaxCodecFramesPerSdu,
         UINT8_TO_VEC_UINT8(codec_frames_per_sdu)},
                       }),
    });
    pac_records_.push_back(
        acs_ac_record({.codec_id = codec_id,
                       .codec_spec_caps = ltv_map,
                       .codec_spec_caps_raw = ltv_map.RawPacket(),
                       .metadata = std::vector<uint8_t>(0)}));
  }

Loading