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

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

Merge "HCI: Hold packets that come before connections"

parents 90e75dc9 b337b236
Loading
Loading
Loading
Loading
+52 −1
Original line number Diff line number Diff line
@@ -103,6 +103,9 @@ struct AclManager::impl {
      classic_impl_ = nullptr;
    }

    unknown_acl_alarm_.reset();
    waiting_packets_.clear();

    delete round_robin_scheduler_;
    hci_queue_end_ = nullptr;
    handler_ = nullptr;
@@ -110,8 +113,44 @@ struct AclManager::impl {
    acl_scheduler_ = nullptr;
  }

  void retry_unknown_acl(bool timed_out) {
    std::vector<AclView> unsent_packets;
    for (const auto& itr : waiting_packets_) {
      auto handle = itr.GetHandle();
      if (!classic_impl_->send_packet_upward(
              handle,
              [itr](struct acl_manager::assembler* assembler) {
                assembler->on_incoming_packet(itr);
              }) &&
          !le_impl_->send_packet_upward(handle, [itr](struct acl_manager::assembler* assembler) {
            assembler->on_incoming_packet(itr);
          })) {
        if (!timed_out) {
          unsent_packets.push_back(itr);
        } else {
          LOG_ERROR(
              "Dropping packet of size %zu to unknown connection 0x%0hx",
              itr.size(),
              itr.GetHandle());
        }
      }
    }
    waiting_packets_ = std::move(unsent_packets);
  }

  static void on_unknown_acl_timer(struct AclManager::impl* impl) {
    LOG_INFO("Timer fired!");
    impl->retry_unknown_acl(/* timed_out = */ true);
    impl->unknown_acl_alarm_.reset();
  }

  // Invoked from some external Queue Reactable context 2
  void dequeue_and_route_acl_packet_to_connection() {
    // Retry any waiting packets first
    if (!waiting_packets_.empty()) {
      retry_unknown_acl(/* timed_out = */ false);
    }

    auto packet = hci_queue_end_->TryDequeue();
    ASSERT(packet != nullptr);
    if (!packet->IsValid()) {
@@ -126,7 +165,16 @@ struct AclManager::impl {
    if (le_impl_->send_packet_upward(
            handle, [&packet](struct acl_manager::assembler* assembler) { assembler->on_incoming_packet(*packet); }))
      return;
    LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), packet->GetHandle());
    if (unknown_acl_alarm_ == nullptr) {
      unknown_acl_alarm_.reset(new os::Alarm(handler_));
    }
    waiting_packets_.push_back(*packet);
    LOG_INFO(
        "Saving packet of size %zu to unknown connection 0x%0hx",
        packet->size(),
        packet->GetHandle());
    unknown_acl_alarm_->Schedule(
        BindOnce(&on_unknown_acl_timer, common::Unretained(this)), kWaitBeforeDroppingUnknownAcl);
  }

  void Dump(
@@ -146,6 +194,9 @@ struct AclManager::impl {
  std::atomic_bool enqueue_registered_ = false;
  uint16_t default_link_policy_settings_ = 0xffff;
  mutable std::mutex dumpsys_mutex_;
  std::unique_ptr<os::Alarm> unknown_acl_alarm_;
  std::vector<AclView> waiting_packets_;
  static constexpr std::chrono::seconds kWaitBeforeDroppingUnknownAcl{1};
};

AclManager::AclManager() : pimpl_(std::make_unique<impl>(*this)) {}
+70 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "hci/controller.h"
#include "hci/hci_layer.h"
#include "hci/hci_layer_fake.h"
#include "os/fake_timer/fake_timerfd.h"
#include "os/thread.h"
#include "packet/raw_builder.h"

@@ -44,6 +45,7 @@ namespace {

using common::BidiQueue;
using common::BidiQueueEnd;
using os::fake_timer::fake_timerfd_advance;
using packet::kLittleEndian;
using packet::PacketView;
using packet::RawBuilder;
@@ -405,6 +407,11 @@ class AclManagerWithLeConnectionTest : public AclManagerTest {
          }
        });

    if (send_early_acl_) {
      LOG_INFO("Sending a packet with handle 0x%02x (0x%d)", handle_, handle_);
      test_hci_layer_->IncomingAclData(handle_);
    }

    test_hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
        ErrorCode::SUCCESS,
        handle_,
@@ -438,11 +445,20 @@ class AclManagerWithLeConnectionTest : public AclManagerTest {
  }

  uint16_t handle_ = 0x123;
  bool send_early_acl_ = false;
  std::shared_ptr<LeAclConnection> connection_;
  AddressWithType remote_with_type_;
  MockLeConnectionManagementCallbacks mock_le_connection_management_callbacks_;
};

class AclManagerWithLateLeConnectionTest : public AclManagerWithLeConnectionTest {
 protected:
  void SetUp() override {
    send_early_acl_ = true;
    AclManagerWithLeConnectionTest::SetUp();
  }
};

// TODO: implement version of this test where controller supports Extended Advertising Feature in
// GetLeLocalSupportedFeatures, and LE Extended Create Connection is used
TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_connection_complete_success) {
@@ -705,6 +721,60 @@ TEST_F(AclManagerWithLeConnectionTest, invoke_registered_callback_le_queue_disco
  sync_client_handler();
}

TEST_F(AclManagerWithLateLeConnectionTest, and_receive_nothing) {}

TEST_F(AclManagerWithLateLeConnectionTest, receive_acl) {
  client_handler_->Post(common::BindOnce(fake_timerfd_advance, 1200));
  auto queue_end = connection_->GetAclQueueEnd();
  std::unique_ptr<PacketView<kLittleEndian>> received;
  do {
    received = queue_end->TryDequeue();
  } while (received == nullptr);

  {
    ASSERT_EQ(received->size(), 10u);
    auto itr = received->begin();
    ASSERT_EQ(itr.extract<uint16_t>(), 6u);  // L2CAP PDU size
    ASSERT_EQ(itr.extract<uint16_t>(), 2u);  // L2CAP CID
    ASSERT_EQ(itr.extract<uint16_t>(), handle_);
    ASSERT_GE(itr.extract<uint32_t>(), 0u);  // packet number
  }
}

TEST_F(AclManagerWithLateLeConnectionTest, receive_acl_in_order) {
  // Send packet #2 from HCI (the first was sent in the test)
  test_hci_layer_->IncomingAclData(handle_);
  auto queue_end = connection_->GetAclQueueEnd();

  std::unique_ptr<PacketView<kLittleEndian>> received;
  do {
    received = queue_end->TryDequeue();
  } while (received == nullptr);

  uint32_t first_packet_number = 0;
  {
    ASSERT_EQ(received->size(), 10u);
    auto itr = received->begin();
    ASSERT_EQ(itr.extract<uint16_t>(), 6u);  // L2CAP PDU size
    ASSERT_EQ(itr.extract<uint16_t>(), 2u);  // L2CAP CID
    ASSERT_EQ(itr.extract<uint16_t>(), handle_);

    first_packet_number = itr.extract<uint32_t>();
  }

  do {
    received = queue_end->TryDequeue();
  } while (received == nullptr);
  {
    ASSERT_EQ(received->size(), 10u);
    auto itr = received->begin();
    ASSERT_EQ(itr.extract<uint16_t>(), 6u);  // L2CAP PDU size
    ASSERT_EQ(itr.extract<uint16_t>(), 2u);  // L2CAP CID
    ASSERT_EQ(itr.extract<uint16_t>(), handle_);
    ASSERT_GT(itr.extract<uint32_t>(), first_packet_number);
  }
}

TEST_F(AclManagerWithConnectionTest, invoke_registered_callback_disconnection_complete) {
  auto reason = ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
  EXPECT_CALL(mock_connection_management_callbacks_, OnDisconnection(reason));