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

Commit 97bfb0a9 authored by Myles Watson's avatar Myles Watson Committed by Gerrit Code Review
Browse files

Merge "ACL: Allow short packets" into main

parents 5600dee8 6a5b594f
Loading
Loading
Loading
Loading
+42 −52
Original line number Diff line number Diff line
@@ -34,23 +34,35 @@ constexpr size_t kMaxQueuedPacketsPerConnection = 10;
constexpr size_t kL2capBasicFrameHeaderSize = 4;

namespace {
// This is a helper class to keep the state of the assembler and expose PacketView<>::Append.
class PacketViewForRecombination : public packet::PacketView<packet::kLittleEndian> {
 public:
  PacketViewForRecombination(const PacketView& packetView) : PacketView(packetView) {}
  PacketViewForRecombination(const PacketView& packetView)
      : PacketView(packetView), received_first_(true) {}

  PacketViewForRecombination()
      : PacketView(PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>())) {}

  void AppendPacketView(packet::PacketView<packet::kLittleEndian> to_append) {
    Append(to_append);
  }

  bool ReceivedFirstPacket() {
    return received_first_;
  }

 private:
  bool received_first_{};
};

// Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall contain L2CAP PDU.
// This function returns the PDU size of the L2CAP data if it's a starting packet. Returns 0 if it's invalid.
uint16_t GetL2capPduSize(AclView packet) {
  auto l2cap_payload = packet.GetPayload();
  if (l2cap_payload.size() < kL2capBasicFrameHeaderSize) {
    LOG_ERROR("Controller sent an invalid L2CAP starting packet!");
    return 0;
// Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall
// contain L2CAP PDU. This function returns the PDU size of the L2CAP starting packet, or
// kL2capBasicFrameHeaderSize if it's invalid.
size_t GetL2capPduSize(packet::PacketView<packet::kLittleEndian> pdu) {
  if (pdu.size() < 2) {
    return kL2capBasicFrameHeaderSize;  // We need at least 4 bytes to send it to L2CAP
  }
  return (l2cap_payload.at(1) << 8u) + l2cap_payload.at(0);
  return (static_cast<size_t>(pdu[1]) << 8u) + pdu[0];
}

}  // namespace
@@ -61,9 +73,7 @@ struct assembler {
  AddressWithType address_with_type_;
  AclConnection::QueueDownEnd* down_end_;
  os::Handler* handler_;
  PacketViewForRecombination recombination_stage_{
      PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>())};
  size_t remaining_sdu_continuation_packet_size_ = 0;
  PacketViewForRecombination recombination_stage_{};
  std::shared_ptr<std::atomic_bool> enqueue_registered_ = std::make_shared<std::atomic_bool>(false);
  std::queue<packet::PacketView<packet::kLittleEndian>> incoming_queue_;

@@ -74,7 +84,7 @@ struct assembler {
  }

  // Invoked from some external Queue Reactable context
  std::unique_ptr<packet::PacketView<packet::kLittleEndian>> on_le_incoming_data_ready() {
  std::unique_ptr<packet::PacketView<packet::kLittleEndian>> on_data_ready() {
    auto packet = incoming_queue_.front();
    incoming_queue_.pop();
    if (incoming_queue_.empty() && enqueue_registered_->exchange(false)) {
@@ -85,7 +95,6 @@ struct assembler {

  void on_incoming_packet(AclView packet) {
    PacketView<packet::kLittleEndian> payload = packet.GetPayload();
    size_t payload_size = payload.size();
    auto broadcast_flag = packet.GetBroadcastFlag();
    if (broadcast_flag == BroadcastFlag::ACTIVE_PERIPHERAL_BROADCAST) {
      LOG_WARN("Dropping broadcast from remote");
@@ -97,58 +106,39 @@ struct assembler {
      return;
    }
    if (packet_boundary_flag == PacketBoundaryFlag::CONTINUING_FRAGMENT) {
      if (remaining_sdu_continuation_packet_size_ < payload_size) {
        LOG_WARN("Remote sent unexpected L2CAP PDU. Drop the entire L2CAP PDU");
        recombination_stage_ =
            PacketViewForRecombination(PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>()));
        remaining_sdu_continuation_packet_size_ = 0;
      if (!recombination_stage_.ReceivedFirstPacket()) {
        LOG_ERROR("Continuing fragment received without previous first, dropping it.");
        return;
      }
      remaining_sdu_continuation_packet_size_ -= payload_size;
      recombination_stage_.AppendPacketView(payload);
      if (remaining_sdu_continuation_packet_size_ != 0) {
        return;
      } else {
        payload = recombination_stage_;
        recombination_stage_ =
            PacketViewForRecombination(PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>()));
      }
    } else if (packet_boundary_flag == PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE) {
      if (recombination_stage_.size() > 0) {
      if (recombination_stage_.ReceivedFirstPacket()) {
        LOG_ERROR("Controller sent a starting packet without finishing previous packet. Drop previous one.");
      }
      size_t l2cap_pdu_size = GetL2capPduSize(packet);
      if (l2cap_pdu_size == 0) {
        LOG_WARN("dropping an invalid L2CAP packet");
        return;
      }

      remaining_sdu_continuation_packet_size_ = l2cap_pdu_size - (payload_size - kL2capBasicFrameHeaderSize);
      if ((payload_size - kL2capBasicFrameHeaderSize) > l2cap_pdu_size) {
        LOG_WARN(
            "Remote presented mismatched packet sizes payload_size:%zu l2cap_pdu_size:%zu",
            payload_size - kL2capBasicFrameHeaderSize,
            l2cap_pdu_size);
        remaining_sdu_continuation_packet_size_ = 0;
      } else {
        remaining_sdu_continuation_packet_size_ =
            l2cap_pdu_size - (payload_size - kL2capBasicFrameHeaderSize);
      }
      if (remaining_sdu_continuation_packet_size_ > 0) {
      recombination_stage_ = payload;
        return;
    }
    // Check the size of the packet
    size_t expected_size = GetL2capPduSize(recombination_stage_) + kL2capBasicFrameHeaderSize;
    if (expected_size < recombination_stage_.size()) {
      LOG_INFO("Packet size doesn't match L2CAP header, dropping it.");
      recombination_stage_ = PacketViewForRecombination();
      return;
    } else if (expected_size > recombination_stage_.size()) {
      // Wait for the next fragment before sending
      return;
    }
    if (incoming_queue_.size() > kMaxQueuedPacketsPerConnection) {
      LOG_ERROR("Dropping packet from %s due to congestion",
                 ADDRESS_TO_LOGGABLE_CSTR(address_with_type_));
      recombination_stage_ = PacketViewForRecombination();
      return;
    }

    incoming_queue_.push(payload);
    incoming_queue_.push(recombination_stage_);
    recombination_stage_ = PacketViewForRecombination();
    if (!enqueue_registered_->exchange(true)) {
      down_end_->RegisterEnqueue(handler_,
                                 common::Bind(&assembler::on_le_incoming_data_ready, common::Unretained(this)));
      down_end_->RegisterEnqueue(
          handler_, common::Bind(&assembler::on_data_ready, common::Unretained(this)));
    }
  }
};
+174 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ using bluetooth::packet::kLittleEndian;
using bluetooth::packet::PacketView;
using bluetooth::packet::RawBuilder;
using testing::_;
using testing::ElementsAreArray;

namespace {

@@ -1387,6 +1388,179 @@ TEST_F(AclManagerWithConnectionTest, remote_esco_connect_request) {
  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
}

class AclManagerWithConnectionAssemblerTest : public AclManagerWithConnectionTest {
 protected:
  void SetUp() override {
    AclManagerWithConnectionTest::SetUp();
    connection_queue_end_ = connection_->GetAclQueueEnd();
  }

  std::vector<uint8_t> MakeAclPayload(size_t length, uint16_t cid, uint8_t offset) {
    std::vector<uint8_t> acl_payload;
    acl_payload.push_back(length & 0xff);
    acl_payload.push_back((length >> 8u) & 0xff);
    acl_payload.push_back(cid & 0xff);
    acl_payload.push_back((cid >> 8u) & 0xff);
    for (uint8_t i = 0; i < length; i++) {
      acl_payload.push_back(i + offset);
    }
    return acl_payload;
  }

  void SendSinglePacket(const std::vector<uint8_t>& acl_payload) {
    auto payload_builder = std::make_unique<RawBuilder>(acl_payload);

    test_hci_layer_->IncomingAclData(
        handle_,
        AclBuilder::Create(
            handle_,
            PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
            BroadcastFlag::POINT_TO_POINT,
            std::move(payload_builder)));
  }

  void ReceiveAndCheckSinglePacket(const std::vector<uint8_t>& acl_payload) {
    std::unique_ptr<PacketView<kLittleEndian>> received;
    do {
      received = connection_queue_end_->TryDequeue();
    } while (received == nullptr);

    std::vector<uint8_t> received_vector;
    for (uint8_t byte : *received) {
      received_vector.push_back(byte);
    }

    EXPECT_THAT(received_vector, ElementsAreArray(acl_payload));
  }

  void SendAndReceiveSinglePacket(const std::vector<uint8_t>& acl_payload) {
    SendSinglePacket(acl_payload);
    ReceiveAndCheckSinglePacket(acl_payload);
  }

  void TearDown() override {
    // Make sure that all previous packets were received and the assembler is in a good state.
    SendAndReceiveSinglePacket(MakeAclPayload(0x60, 0xACC, 3));
    AclManagerWithConnectionTest::TearDown();
  }
  AclConnection::QueueUpEnd* connection_queue_end_{};
};

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_single_packet) {}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_short_packet_discarded) {
  std::vector<uint8_t> invalid_payload{1, 2};
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(invalid_payload)));
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_two_short_packets_discarded) {
  std::vector<uint8_t> invalid_payload{1, 2};
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(invalid_payload)));
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(invalid_payload)));
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_single_valid_packet) {
  SendAndReceiveSinglePacket(MakeAclPayload(20, 0x41, 2));
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_one_byte_packets) {
  size_t payload_size = 0x30;
  std::vector<uint8_t> payload = MakeAclPayload(payload_size, 0xABB /* cid */, 4 /* offset */);
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(
              std::vector<uint8_t>{payload.cbegin(), payload.cbegin() + 1})));
  for (size_t i = 1; i < payload.size(); i++) {
    test_hci_layer_->IncomingAclData(
        handle_,
        AclBuilder::Create(
            handle_,
            PacketBoundaryFlag::CONTINUING_FRAGMENT,
            BroadcastFlag::POINT_TO_POINT,
            std::make_unique<RawBuilder>(
                std::vector<uint8_t>{payload.cbegin() + i, payload.cbegin() + i + 1})));
  }
  ReceiveAndCheckSinglePacket(payload);
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_two_byte_packets) {
  size_t payload_size = 0x30;  // must be even
  std::vector<uint8_t> payload = MakeAclPayload(payload_size, 0xABB /* cid */, 4 /* offset */);
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(
              std::vector<uint8_t>{payload.cbegin(), payload.cbegin() + 2})));
  for (size_t i = 1; i < payload.size() / 2; i++) {
    test_hci_layer_->IncomingAclData(
        handle_,
        AclBuilder::Create(
            handle_,
            PacketBoundaryFlag::CONTINUING_FRAGMENT,
            BroadcastFlag::POINT_TO_POINT,
            std::make_unique<RawBuilder>(
                std::vector<uint8_t>{payload.cbegin() + 2 * i, payload.cbegin() + 2 * (i + 1)})));
  }
  ReceiveAndCheckSinglePacket(payload);
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_continuation_without_begin) {
  size_t payload_size = 0x30;
  std::vector<uint8_t> payload = MakeAclPayload(payload_size, 0xABB /* cid */, 4 /* offset */);
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::CONTINUING_FRAGMENT,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(std::vector<uint8_t>{payload.cbegin(), payload.cend()})));
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_drop_broadcasts) {
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::ACTIVE_PERIPHERAL_BROADCAST,
          std::make_unique<RawBuilder>(MakeAclPayload(20, 0xBBB /* cid */, 5 /* offset */))));
}

TEST_F(AclManagerWithConnectionAssemblerTest, assembler_test_drop_non_flushable) {
  test_hci_layer_->IncomingAclData(
      handle_,
      AclBuilder::Create(
          handle_,
          PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE,
          BroadcastFlag::POINT_TO_POINT,
          std::make_unique<RawBuilder>(MakeAclPayload(20, 0xAAA /* cid */, 6 /* offset */))));
}

}  // namespace acl_manager
}  // namespace hci
}  // namespace bluetooth
+12 −4
Original line number Diff line number Diff line
@@ -168,28 +168,36 @@ void TestHciLayer::InitEmptyCommand() {
  ASSERT_TRUE(empty_command_view_.IsValid());
}

void TestHciLayer::IncomingAclData(uint16_t handle) {
void TestHciLayer::IncomingAclData(uint16_t handle, std::unique_ptr<AclBuilder> acl_builder) {
  os::Handler* hci_handler = GetHandler();
  auto* queue_end = acl_queue_.GetDownEnd();
  std::promise<void> promise;
  auto future = promise.get_future();
  auto packet = GetPacketView(std::move(acl_builder));
  auto acl_view = AclView::Create(packet);
  queue_end->RegisterEnqueue(
      hci_handler,
      common::Bind(
          [](decltype(queue_end) queue_end, uint16_t handle, std::promise<void> promise) {
            auto packet = GetPacketView(NextAclPacket(handle));
            AclView acl2 = AclView::Create(packet);
          [](decltype(queue_end) queue_end,
             uint16_t handle,
             AclView acl2,
             std::promise<void> promise) {
            queue_end->UnregisterEnqueue();
            promise.set_value();
            return std::make_unique<AclView>(acl2);
          },
          queue_end,
          handle,
          acl_view,
          common::Passed(std::move(promise))));
  auto status = future.wait_for(std::chrono::milliseconds(1000));
  ASSERT_EQ(status, std::future_status::ready);
}

void TestHciLayer::IncomingAclData(uint16_t handle) {
  IncomingAclData(handle, NextAclPacket(handle));
}

void TestHciLayer::AssertNoOutgoingAclData() {
  auto queue_end = acl_queue_.GetDownEnd();
  EXPECT_EQ(queue_end->TryDequeue(), nullptr);
+2 −0
Original line number Diff line number Diff line
@@ -61,6 +61,8 @@ class TestHciLayer : public HciLayer {

  void IncomingAclData(uint16_t handle);

  void IncomingAclData(uint16_t handle, std::unique_ptr<AclBuilder> acl_builder);

  void AssertNoOutgoingAclData();

  packet::PacketView<packet::kLittleEndian> OutgoingAclData();