| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/transport/acl_data_channel.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/fpromise/single_threaded_executor.h> |
| #include <lib/inspect/testing/cpp/inspect.h> |
| #include <zircon/assert.h> |
| |
| #include <unordered_map> |
| |
| #include <gmock/gmock.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/defaults.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/controller_test.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/mock_controller.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/test_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/link_type.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/transport.h" |
| |
| namespace bt::hci { |
| namespace { |
| |
| constexpr hci_spec::ConnectionHandle kLinkHandle = 0x0001; |
| |
| using TestingBase = bt::testing::ControllerTest<bt::testing::MockController>; |
| |
| class ACLDataChannelTest : public TestingBase { |
| public: |
| ACLDataChannelTest() = default; |
| ~ACLDataChannelTest() override = default; |
| |
| protected: |
| // TestBase overrides: |
| void SetUp() override { |
| TestingBase::SetUp(); |
| StartTestDevice(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ACLDataChannelTest); |
| }; |
| |
| using HCI_ACLDataChannelTest = ACLDataChannelTest; |
| |
| TEST_F(ACLDataChannelTest, VerifyMTUs) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| const DataBufferInfo kLEBufferInfo(64, 16); |
| |
| // BR/EDR buffer only. |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| EXPECT_EQ(kBREDRBufferInfo, acl_data_channel()->GetBufferInfo()); |
| EXPECT_EQ(kBREDRBufferInfo, acl_data_channel()->GetLeBufferInfo()); |
| |
| TearDown(); |
| SetUp(); |
| |
| // LE buffer only. |
| InitializeACLDataChannel(DataBufferInfo(), kLEBufferInfo); |
| EXPECT_EQ(DataBufferInfo(), acl_data_channel()->GetBufferInfo()); |
| EXPECT_EQ(kLEBufferInfo, acl_data_channel()->GetLeBufferInfo()); |
| |
| TearDown(); |
| SetUp(); |
| |
| // Both buffers available. |
| InitializeACLDataChannel(kBREDRBufferInfo, kLEBufferInfo); |
| EXPECT_EQ(kBREDRBufferInfo, acl_data_channel()->GetBufferInfo()); |
| EXPECT_EQ(kLEBufferInfo, acl_data_channel()->GetLeBufferInfo()); |
| } |
| |
| // Test that SendPacket works using only the BR/EDR buffer. |
| TEST_F(ACLDataChannelTest, SendPacketBREDRBuffer) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| int handle0_packet_count = 0; |
| int handle1_packet_count = 0; |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Queue up 10 packets in total, distributed among the two connection handles. |
| async::PostTask(dispatcher(), [this] { |
| for (int i = 0; i < 10; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| }); |
| |
| RunLoopUntilIdle(); |
| |
| // kMaxNumPackets is 5. The controller should have received 3 packets on |
| // kHandle0 and 2 on kHandle1 |
| EXPECT_EQ(3, handle0_packet_count); |
| EXPECT_EQ(2, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(5, handle0_packet_count); |
| EXPECT_EQ(5, handle1_packet_count); |
| } |
| |
| inspect::Hierarchy ReadInspect(inspect::Inspector& inspector) { |
| fpromise::single_threaded_executor executor; |
| fpromise::result<inspect::Hierarchy> hierarchy; |
| executor.schedule_task(inspect::ReadFromInspector(inspector).then( |
| [&](fpromise::result<inspect::Hierarchy>& res) { hierarchy = std::move(res); })); |
| executor.run(); |
| ZX_ASSERT(hierarchy.is_ok()); |
| return hierarchy.take_value(); |
| } |
| |
| // Test that SendPacket works using the LE buffer when no BR/EDR buffer is |
| // available. |
| TEST_F(ACLDataChannelTest, SendPacketLEBuffer) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kTotalAttempts = 12; |
| constexpr size_t kTotalExpected = 6; |
| constexpr size_t kBufferNumPackets = 3; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(), DataBufferInfo(kMaxMTU, kBufferNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // This should fail because the payload exceeds the MTU. |
| auto packet = ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU + 1); |
| EXPECT_FALSE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| |
| size_t handle0_packet_count = 0; |
| size_t handle1_packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Queue up 12 packets in total, distributed among the two connection handles |
| // and link types. Since the BR/EDR MTU is zero, we expect to only see LE |
| // packets transmitted. |
| async::PostTask(dispatcher(), [this] { |
| for (size_t i = 0; i < kTotalAttempts; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| if (i % 2) { |
| // ACL-U packets should fail due to 0 MTU size. |
| EXPECT_FALSE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } else { |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| } |
| }); |
| |
| RunLoopUntilIdle(); |
| |
| // The controller can buffer 3 packets. Since BR/EDR packets should have |
| // failed to go out, the controller should have received 3 packets on handle 0 |
| // and none on handle 1. |
| EXPECT_EQ(kTotalExpected / 2u, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x05, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x03, |
| 0x00 // 3 packets on handle 0x0001 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(kTotalExpected, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| } |
| |
| // Test that SendPacket works for LE packets when both buffer types are |
| // available. |
| TEST_F(ACLDataChannelTest, SendLEPacketBothBuffers) { |
| constexpr size_t kMaxMTU = 200; |
| constexpr size_t kMaxNumPackets = 50; |
| constexpr size_t kLEMaxMTU = 5; |
| constexpr size_t kLEMaxNumPackets = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), |
| DataBufferInfo(kLEMaxMTU, kLEMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kLE); |
| |
| // This should fail because the payload exceeds the LE MTU. |
| auto packet = ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_FALSE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| |
| int handle0_packet_count = 0; |
| int handle1_packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Queue up 10 packets in total, distributed among the two connection handles. |
| async::PostTask(dispatcher(), [this] { |
| for (int i = 0; i < 10; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kLEMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| }); |
| |
| RunLoopUntilIdle(); |
| |
| // ACLDataChannel should be looking at kLEMaxNumPackets, which is 5. The |
| // controller should have received 3 packets on kHandle0 and 2 on kHandle1 |
| EXPECT_EQ(3, handle0_packet_count); |
| EXPECT_EQ(2, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(5, handle0_packet_count); |
| EXPECT_EQ(5, handle1_packet_count); |
| } |
| |
| // Test that SendPacket works for BR/EDR packets when both buffer types are |
| // available. |
| TEST_F(ACLDataChannelTest, SendBREDRPacketBothBuffers) { |
| constexpr size_t kLEMaxMTU = 200; |
| constexpr size_t kLEMaxNumPackets = 50; |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), |
| DataBufferInfo(kLEMaxMTU, kLEMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // This should fail because the payload exceeds the ACL MTU. |
| auto packet = ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kLEMaxMTU); |
| EXPECT_FALSE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| |
| int handle0_packet_count = 0; |
| int handle1_packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Queue up 10 packets in total, distributed among the two connection handles. |
| async::PostTask(dispatcher(), [this] { |
| for (int i = 0; i < 10; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| }); |
| |
| RunLoopUntilIdle(); |
| |
| // ACLDataChannel should be looking at kLEMaxNumPackets, which is 5. The |
| // controller should have received 3 packets on kHandle0 and 2 on kHandle1 |
| EXPECT_EQ(3, handle0_packet_count); |
| EXPECT_EQ(2, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(5, handle0_packet_count); |
| EXPECT_EQ(5, handle1_packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, SendPacketsFailure) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr hci_spec::ConnectionHandle kHandle = 0x0001; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kACL); |
| |
| // Empty packet list. |
| EXPECT_FALSE(acl_data_channel()->SendPackets( |
| LinkedList<ACLDataPacket>(), l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| // Packet exceeds MTU |
| LinkedList<ACLDataPacket> packets; |
| packets.push_back(ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU + 1)); |
| EXPECT_FALSE(acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| // Suffix DeathTest has GoogleTest-specific behavior |
| using ACLDataChannelDeathTest = HCI_ACLDataChannelTest; |
| |
| TEST_F(ACLDataChannelDeathTest, SendPacketsCrashesWithContinuingFragments) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr hci_spec::ConnectionHandle kHandle = 0x0001; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kACL); |
| |
| LinkedList<ACLDataPacket> packets; |
| packets.push_back(ACLDataPacket::New(kHandle, |
| hci_spec::ACLPacketBoundaryFlag::kContinuingFragment, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU)); |
| ASSERT_DEATH_IF_SUPPORTED( |
| acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow), |
| "expected full PDU"); |
| } |
| |
| TEST_F(ACLDataChannelDeathTest, SendPacketsCrashesWithPacketsForMoreThanOneConnection) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // Packet exceeds MTU |
| LinkedList<ACLDataPacket> packets; |
| packets.push_back(ACLDataPacket::New(kHandle0, |
| hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU)); |
| packets.push_back(ACLDataPacket::New(kHandle1, |
| hci_spec::ACLPacketBoundaryFlag::kContinuingFragment, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU)); |
| ASSERT_DEATH_IF_SUPPORTED( |
| acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow), |
| "expected only fragments for one connection"); |
| } |
| |
| // Tests sending multiple packets in a single call. |
| TEST_F(ACLDataChannelTest, SendPackets) { |
| constexpr int kExpectedPacketCount = 5; |
| constexpr hci_spec::ConnectionHandle kHandle = 0x0001; |
| |
| InitializeACLDataChannel(DataBufferInfo(1024, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kLE); |
| |
| bool pass = true; |
| int seq_no = 0; |
| auto data_cb = [&pass, &seq_no](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| EXPECT_EQ(1u, packet.payload_size()); |
| |
| int cur_no = packet.payload_data().data()[0]; |
| if (cur_no != seq_no + 1) { |
| pass = false; |
| return; |
| } |
| |
| seq_no = cur_no; |
| }; |
| test_device()->SetDataCallback(data_cb, dispatcher()); |
| |
| LinkedList<ACLDataPacket> packets; |
| for (int i = 1; i <= kExpectedPacketCount; ++i) { |
| auto packet = ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1); |
| packet->mutable_view()->mutable_payload_bytes()[0] = i; |
| packets.push_back(std::move(packet)); |
| } |
| |
| EXPECT_TRUE(acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(pass); |
| EXPECT_EQ(kExpectedPacketCount, seq_no); |
| } |
| |
| TEST_F(ACLDataChannelTest, |
| UnregisterLinkDoesNotClearNumSentPacketsAndClearControllerPacketCountDoes) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 1; |
| constexpr hci_spec::ConnectionHandle kHandle2 = 2; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle2, bt::LinkType::kLE); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| // Send 3 packets on two links. This is enough to fill up the data buffers. |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle2, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // The third packet should have been queued. |
| ASSERT_EQ(2, packet_count); |
| |
| // UnregisterLink should not decrement sent packet count, |
| // so next packet should not be sent. |
| acl_data_channel()->UnregisterLink(kHandle2); |
| RunLoopUntilIdle(); |
| ASSERT_EQ(2, packet_count); |
| |
| // Clear the controller packet count for |kHandle2|. The next packet should go out. |
| acl_data_channel()->ClearControllerPacketCount(kHandle2); |
| RunLoopUntilIdle(); |
| ASSERT_EQ(3, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, SendingPacketsOnUnregisteredLinkDropsPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| // Send packet with unregistered handle. |
| ASSERT_FALSE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // Packet should not have been sent. |
| ASSERT_EQ(0, packet_count); |
| |
| // Now register link. Packet should have been dropped and should still |
| // not be sent. |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kLE); |
| |
| RunLoopUntilIdle(); |
| |
| // Packet should not have been sent. |
| ASSERT_EQ(0, packet_count); |
| |
| // Unregister a link that has not been registered |
| acl_data_channel()->UnregisterLink(kHandle); |
| RunLoopUntilIdle(); |
| ASSERT_EQ(0, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, UnregisterLinkClearsPendingPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 1; |
| constexpr hci_spec::ConnectionHandle kHandle2 = 2; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle2, bt::LinkType::kLE); |
| |
| int handle1_packet_count = 0; |
| int handle2_packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle1) { |
| handle1_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle2, connection_handle); |
| handle2_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Send 3 packets on two links. This is enough to fill up the data buffers. |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle2, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // Only kHandle1 packet should have been sent |
| ASSERT_EQ(1, handle1_packet_count); |
| ASSERT_EQ(0, handle2_packet_count); |
| |
| // Clear pending packet for |kHandle2| |
| acl_data_channel()->UnregisterLink(kHandle2); |
| |
| // Notify the processed packet with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x05, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x01, 0x00 // 1 packet on handle 0x0001 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| // second |kHandle1| packet should have been sent |
| ASSERT_EQ(2, handle1_packet_count); |
| ASSERT_EQ(0, handle2_packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, PacketsQueuedByFlowControlAreNotSentAfterUnregisterLink) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kLE); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // The second packet should have been queued. |
| ASSERT_EQ(1, packet_count); |
| |
| // Clear the packet count for |kHandle2|. The second packet should NOT go out. |
| acl_data_channel()->UnregisterLink(kHandle1); |
| |
| // Notify the processed packet with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x05, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x01, 0x00 // 1 packet on handle 0x0001 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| // The second packet should not have been sent |
| ASSERT_EQ(1, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, |
| StalePacketsBufferedBeforeFirstUnregisterAndBeforeSecondRegisterAreNotSent) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr hci_spec::ConnectionHandle kHandle = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kLE); |
| |
| // Unique packet to send to re-registered link with same handle. |
| const StaticByteBuffer kPacket( |
| // ACL data header (handle: 1, length 1) |
| 0x01, 0x00, 0x01, 0x00, |
| |
| // Unique payload to distinguish this packet from stale packet |
| 0x01); |
| |
| int data_cb_count = 0; |
| auto data_cb = [&](const ByteBuffer& packet) { |
| data_cb_count++; |
| if (data_cb_count == 2) { |
| EXPECT_TRUE(ContainersEqual(kPacket, packet)); |
| } |
| }; |
| test_device()->SetDataCallback(data_cb, dispatcher()); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // The second packet should have been queued. |
| ASSERT_EQ(1, data_cb_count); |
| |
| // Clear the packet count for |kHandle2|. The second packet should NOT go out. |
| acl_data_channel()->UnregisterLink(kHandle); |
| |
| // Notify the processed packet with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x05, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x01, 0x00 // 1 packet on handle 0x0001 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| // The second packet should not have been sent |
| ASSERT_EQ(1, data_cb_count); |
| |
| // Re-Register same link handle |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kLE); |
| |
| auto packet = ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1); |
| packet->mutable_view()->mutable_payload_data().Fill(1); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| |
| // The new packet should have been sent to the MockController. |
| ASSERT_EQ(2, data_cb_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, UnregisterLinkDropsFutureSentPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr hci_spec::ConnectionHandle kHandle = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kLE); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| ASSERT_EQ(1, packet_count); |
| |
| acl_data_channel()->UnregisterLink(kHandle); |
| |
| // attempt to send packet on unregistered link |
| ASSERT_FALSE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| // second packet should not have been sent |
| ASSERT_EQ(1, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, ReceiveData) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| |
| // It doesn't matter what we set the buffer values to since we're testing |
| // incoming packets. |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| constexpr size_t kExpectedPacketCount = 2u; |
| size_t num_rx_packets = 0u; |
| hci_spec::ConnectionHandle packet0_handle; |
| hci_spec::ConnectionHandle packet1_handle; |
| auto data_rx_cb = [&](ACLDataPacketPtr packet) { |
| num_rx_packets++; |
| if (num_rx_packets == 1) { |
| packet0_handle = packet->connection_handle(); |
| } else if (num_rx_packets == 2) { |
| packet1_handle = packet->connection_handle(); |
| } else { |
| ZX_PANIC("|num_rx_packets| has unexpected value: %zu", num_rx_packets); |
| } |
| }; |
| set_data_received_callback(std::move(data_rx_cb)); |
| |
| // Malformed packet: smaller than the ACL header. |
| StaticByteBuffer invalid0(0x01, 0x00, 0x00); |
| |
| // Malformed packet: the payload size given in the header doesn't match the |
| // actual payload size. |
| StaticByteBuffer invalid1(0x01, 0x00, 0x02, 0x00, 0x00); |
| |
| // Valid packet on handle 1. |
| StaticByteBuffer valid0(0x01, 0x00, 0x01, 0x00, 0x00); |
| |
| // Valid packet on handle 2. |
| StaticByteBuffer valid1(0x02, 0x00, 0x01, 0x00, 0x00); |
| |
| async::PostTask(dispatcher(), [&, this] { |
| test_device()->SendACLDataChannelPacket(invalid0); |
| test_device()->SendACLDataChannelPacket(invalid1); |
| test_device()->SendACLDataChannelPacket(valid0); |
| test_device()->SendACLDataChannelPacket(valid1); |
| }); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(kExpectedPacketCount, num_rx_packets); |
| EXPECT_EQ(0x0001, packet0_handle); |
| EXPECT_EQ(0x0002, packet1_handle); |
| } |
| |
| TEST_F(ACLDataChannelTest, TransportClosedCallbackBothChannels) { |
| InitializeACLDataChannel(DataBufferInfo(1u, 1u), DataBufferInfo(1u, 1u)); |
| |
| int closed_cb_count = 0; |
| auto closed_cb = [&closed_cb_count] { closed_cb_count++; }; |
| transport()->SetTransportClosedCallback(closed_cb); |
| |
| async::PostTask(dispatcher(), [this] { test_device()->Stop(ZX_ERR_PEER_CLOSED); }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(1, closed_cb_count); |
| } |
| |
| // Make sure that a HCI "Number of completed packets" event received after shut |
| // down does not cause a crash. |
| TEST_F(ACLDataChannelTest, HciEventReceivedAfterShutDown) { |
| InitializeACLDataChannel(DataBufferInfo(1u, 1u), DataBufferInfo(1u, 1u)); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| ); |
| |
| // Shuts down ACLDataChannel and CommandChannel. |
| DeleteTransport(); |
| |
| test_device()->SendCommandChannelPacket(event_buffer); |
| RunLoopUntilIdle(); |
| } |
| |
| TEST_F(ACLDataChannelTest, DropQueuedPacketsRemovesPacketsMatchingFilterFromQueue) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| constexpr l2cap::ChannelId kChanId0 = 0x40; |
| constexpr l2cap::ChannelId kChanId1 = 0x41; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| int handle0_packet_count = 0; |
| int handle1_packet_count = 0; |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Queue up 10 packets in total, distributed among the two connection handles. |
| for (size_t i = 0; i < 2 * kMaxNumPackets; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), (i % 2) ? kChanId1 : kChanId0, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| RunLoopUntilIdle(); |
| |
| // kMaxNumPackets is 5. The controller should have received 3 packets on |
| // kHandle0 and 2 on kHandle1 |
| EXPECT_EQ(3, handle0_packet_count); |
| EXPECT_EQ(2, handle1_packet_count); |
| |
| // Should remove 3 |kHandle1| packets from queue |
| size_t predicate_count = 0; |
| size_t predicate_true_count = 0; |
| acl_data_channel()->DropQueuedPackets([&](const ACLDataPacketPtr& packet, l2cap::ChannelId id) { |
| predicate_count++; |
| // Verify that correct channels are passed to filter lambda |
| if (packet->connection_handle() == kHandle0) { |
| EXPECT_EQ(id, kChanId0); |
| } else if (packet->connection_handle() == kHandle1) { |
| EXPECT_EQ(id, kChanId1); |
| } |
| |
| bool result = packet->connection_handle() == kHandle1; |
| if (result) { |
| predicate_true_count++; |
| } |
| return result; |
| }); |
| // Should be called for each packet in queue (2 |kHandle0| packets + 3 |kHandle1| packets) |
| EXPECT_EQ(predicate_count, 5u); |
| // 3 |kHandle1| packets |
| EXPECT_EQ(predicate_true_count, 3u); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(5, handle0_packet_count); |
| // Other 3 |kHandle1| packets should have been filtered out of queue |
| EXPECT_EQ(2, handle1_packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, HighPriorityPacketsQueuedAfterLowPriorityPacketsAreSentFirst) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| size_t handle0_packet_count = 0; |
| size_t handle1_packet_count = 0; |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Fill controller with |kMaxNumPackets| packets so queue can grow. |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(kMaxNumPackets, handle0_packet_count); |
| handle0_packet_count = 0; |
| |
| // Queue up 10 packets in total, distributed among the two connection handles. |
| for (size_t i = 0; i < 2 * kMaxNumPackets; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto priority = |
| (i % 2) ? AclDataChannel::PacketPriority::kLow : AclDataChannel::PacketPriority::kHigh; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE( |
| acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, priority)); |
| } |
| |
| RunLoopUntilIdle(); |
| |
| // No packets should have been sent because controller buffer is full. |
| EXPECT_EQ(0u, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x05, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x05, 0x00 // 5 packets on handle 0x0001 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| |
| // Only high priority packets should have been sent. |
| EXPECT_EQ(5u, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| test_device()->SendCommandChannelPacket(event_buffer); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(5u, handle0_packet_count); |
| // Now low priority packets should have been sent. |
| EXPECT_EQ(5u, handle1_packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, OutOfBoundsPacketCountsIgnored) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 6; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| size_t handle0_packet_count = 0; |
| size_t handle1_packet_count = 0; |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci_spec::ACLDataHeader)); |
| |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| if (connection_handle == kHandle0) { |
| handle0_packet_count++; |
| } else { |
| ASSERT_EQ(kHandle1, connection_handle); |
| handle1_packet_count++; |
| } |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Fill controller with |kMaxNumPackets| packets split evenly between the two handles. |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| hci_spec::ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(kMaxNumPackets / 2, handle0_packet_count); |
| EXPECT_EQ(kMaxNumPackets / 2, handle1_packet_count); |
| handle0_packet_count = 0; |
| handle1_packet_count = 0; |
| |
| // Queue up 3 packets for each handle, |
| for (size_t i = 0; i < kMaxNumPackets / 2; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| for (size_t i = 0; i < kMaxNumPackets / 2; ++i) { |
| auto packet = ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| RunLoopUntilIdle(); |
| |
| // No packets should have been sent because controller buffer is full. |
| EXPECT_EQ(0u, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x01, // Number of handles |
| 0x01, 0x00, 0x03, 0x00, // 3 packets on handle 0x0001 |
| 0x02, 0x00, 0x05, 0x00 // (ignored, not indicated in handle count) |
| ); |
| |
| test_device()->SendCommandChannelPacket(event_buffer); |
| RunLoopUntilIdle(); |
| |
| // Only packets on handle0 should have been sent. |
| EXPECT_EQ(3u, handle0_packet_count); |
| EXPECT_EQ(0u, handle1_packet_count); |
| |
| StaticByteBuffer short_buffer(0x13, 0x05, // Event header |
| 0x02, // Number of handles |
| 0x02, 0x00, 0x02, 0x00 // 2 packets on handle 0x0002 |
| // (missing second handle, should be ignored) |
| ); |
| |
| test_device()->SendCommandChannelPacket(short_buffer); |
| RunLoopUntilIdle(); |
| |
| // handle1 packets should have been sent anyway |
| EXPECT_EQ(3u, handle0_packet_count); |
| EXPECT_EQ(2u, handle1_packet_count); |
| } |
| |
| class AclPriorityTest : public HCI_ACLDataChannelTest, |
| public ::testing::WithParamInterface<std::pair<hci::AclPriority, bool>> {}; |
| TEST_P(AclPriorityTest, RequestAclPriority) { |
| const auto kPriority = GetParam().first; |
| const bool kExpectSuccess = GetParam().second; |
| |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| |
| // Arbitrary command payload larger than hci_spec::CommandHeader. |
| const auto op_code = hci_spec::VendorOpCode(0x01); |
| const StaticByteBuffer kEncodedCommand(LowerBits(op_code), UpperBits(op_code), // op code |
| 0x04, // parameter size |
| 0x00, 0x01, 0x02, 0x03); // test parameter |
| |
| constexpr hci_spec::ConnectionHandle kLinkHandle = 0x0001; |
| |
| std::optional<hci_spec::ConnectionHandle> connection; |
| std::optional<hci::AclPriority> priority; |
| set_encode_acl_priority_command_cb( |
| [&](hci_spec::ConnectionHandle cb_connection, hci::AclPriority cb_priority) { |
| connection = cb_connection; |
| priority = cb_priority; |
| return fitx::ok(DynamicByteBuffer(kEncodedCommand)); |
| }); |
| |
| auto cmd_complete = bt::testing::CommandCompletePacket( |
| op_code, |
| kExpectSuccess ? hci_spec::StatusCode::kSuccess : hci_spec::StatusCode::kUnknownCommand); |
| EXPECT_CMD_PACKET_OUT(test_device(), kEncodedCommand, &cmd_complete); |
| |
| size_t request_cb_count = 0; |
| acl_data_channel()->RequestAclPriority(kPriority, kLinkHandle, [&](auto result) { |
| request_cb_count++; |
| EXPECT_EQ(kExpectSuccess, result.is_ok()); |
| }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(request_cb_count, 1u); |
| ASSERT_TRUE(connection); |
| EXPECT_EQ(connection.value(), kLinkHandle); |
| ASSERT_TRUE(priority); |
| EXPECT_EQ(priority.value(), kPriority); |
| } |
| |
| const std::array<std::pair<hci::AclPriority, bool>, 4> kPriorityParams = { |
| {{hci::AclPriority::kSource, /*expect_success=*/false}, |
| {hci::AclPriority::kSource, true}, |
| {hci::AclPriority::kSink, true}, |
| {hci::AclPriority::kNormal, true}}}; |
| INSTANTIATE_TEST_SUITE_P(ACLDataChannelTest, AclPriorityTest, ::testing::ValuesIn(kPriorityParams)); |
| |
| TEST_F(ACLDataChannelTest, RequestAclPriorityEncodeFails) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| |
| set_encode_acl_priority_command_cb([&](auto, auto) { return fitx::error(ZX_ERR_INTERNAL); }); |
| |
| size_t request_cb_count = 0; |
| acl_data_channel()->RequestAclPriority(hci::AclPriority::kSink, kLinkHandle, [&](auto result) { |
| request_cb_count++; |
| EXPECT_TRUE(result.is_error()); |
| }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(request_cb_count, 1u); |
| } |
| |
| TEST_F(ACLDataChannelTest, RequestAclPriorityEncodeReturnsTooSmallBuffer) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| |
| set_encode_acl_priority_command_cb( |
| [](auto, auto) { return fitx::ok(DynamicByteBuffer(StaticByteBuffer(0x00))); }); |
| |
| size_t request_cb_count = 0; |
| acl_data_channel()->RequestAclPriority(hci::AclPriority::kSink, kLinkHandle, [&](auto result) { |
| request_cb_count++; |
| EXPECT_TRUE(result.is_error()); |
| }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(request_cb_count, 1u); |
| } |
| |
| TEST_F(ACLDataChannelTest, SetAutomaticFlushTimeout) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| acl_data_channel()->RegisterLink(kLinkHandle, bt::LinkType::kACL); |
| |
| std::optional<Result<>> cb_status; |
| auto result_cb = [&](auto status) { cb_status = status; }; |
| |
| // Test command complete error |
| const auto kCommandCompleteError = bt::testing::CommandCompletePacket( |
| hci_spec::kWriteAutomaticFlushTimeout, hci_spec::StatusCode::kUnknownConnectionId); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| bt::testing::WriteAutomaticFlushTimeoutPacket(kLinkHandle, 0), |
| &kCommandCompleteError); |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(zx::duration::infinite(), kLinkHandle, |
| result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| ASSERT_TRUE(cb_status->is_error()); |
| EXPECT_EQ(ToResult(hci_spec::StatusCode::kUnknownConnectionId), *cb_status); |
| cb_status.reset(); |
| |
| // Test flush timeout = 0 (no command should be sent) |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(zx::msec(0), kLinkHandle, result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_TRUE(cb_status->is_error()); |
| EXPECT_EQ(ToResult(hci_spec::StatusCode::kInvalidHCICommandParameters), *cb_status); |
| |
| // Test infinite flush timeout (flush timeout of 0 should be sent). |
| const auto kCommandComplete = bt::testing::CommandCompletePacket( |
| hci_spec::kWriteAutomaticFlushTimeout, hci_spec::StatusCode::kSuccess); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| bt::testing::WriteAutomaticFlushTimeoutPacket(kLinkHandle, 0), |
| &kCommandComplete); |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(zx::duration::infinite(), kLinkHandle, |
| result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_EQ(fitx::ok(), *cb_status); |
| cb_status.reset(); |
| |
| // Test msec to parameter conversion (hci_spec::kMaxAutomaticFlushTimeoutDuration(1279) * |
| // conversion_factor(1.6) = 2046). |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| bt::testing::WriteAutomaticFlushTimeoutPacket(kLinkHandle, 2046), |
| &kCommandComplete); |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(hci_spec::kMaxAutomaticFlushTimeoutDuration, |
| kLinkHandle, result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_EQ(fitx::ok(), *cb_status); |
| cb_status.reset(); |
| |
| // Test too large flush timeout (no command should be sent). |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout( |
| hci_spec::kMaxAutomaticFlushTimeoutDuration + zx::msec(1), kLinkHandle, result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_TRUE(cb_status->is_error()); |
| EXPECT_EQ(ToResult(hci_spec::StatusCode::kInvalidHCICommandParameters), *cb_status); |
| } |
| |
| TEST_F(ACLDataChannelTest, |
| SendingLowPriorityBrEdrPacketsWhenTooManyAreQueuedDropsLeastRecentlySentPduOnSameChannel) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets), |
| DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| inspect::Inspector inspector; |
| acl_data_channel()->AttachInspect(inspector.GetRoot(), AclDataChannel::kInspectNodeName); |
| |
| // Fill up both LE and BR/EDR controller buffers |
| for (hci_spec::ConnectionHandle handle : {kHandle0, kHandle1}) { |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| } |
| RunLoopUntilIdle(); |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| size_t packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ASSERT_LE(sizeof(hci_spec::ACLDataHeader), bytes.size()); |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| // LE is still waiting for controller credits |
| EXPECT_EQ(kHandle1, connection_handle); |
| |
| if ((packet_count == 0) || (packet_count == 1)) { |
| // The first low-priority queued packet and its continuation packet were dropped so the first |
| // packets actually sent should be those for the second PDU |
| EXPECT_EQ(1u, packet.payload_data()[0]); |
| } |
| |
| packet_count++; |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Send enough data that the first PDU sent in this loop gets dropped |
| for (size_t i = 0; i < AclDataChannel::kMaxAclPacketsPerChannel + 1; ++i) { |
| // Send two fragments per PDU |
| LinkedList<ACLDataPacket> packets; |
| for (auto pbf : {hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLPacketBoundaryFlag::kContinuingFragment}) { |
| auto packet = |
| ACLDataPacket::New(kHandle1, pbf, hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| |
| // Write a sequence number into the payload, starting at 0 |
| packet->mutable_view()->mutable_payload_data()[0] = static_cast<uint8_t>(i); |
| packets.push_back(std::move(packet)); |
| } |
| EXPECT_TRUE(acl_data_channel()->SendPackets(std::move(packets), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| test_device()->SendCommandChannelPacket(bt::testing::NumberOfCompletedPacketsPacket(kHandle1, 2)); |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, packet_count); |
| |
| using namespace ::inspect::testing; |
| auto adc_matcher = NodeMatches( |
| PropertyList(AllOf(Contains(UintIs("num_overflow_packets", packet_count)), |
| Contains(UintIs("num_recent_overflow_packets", packet_count))))); |
| |
| auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo()).take_value(); |
| EXPECT_THAT(hierarchy, ChildrenMatch(ElementsAre(adc_matcher))); |
| } |
| |
| TEST_F(ACLDataChannelTest, |
| SendingLowPriorityPacketsThatDropDoNotAffectDataOnSameLinkDifferentChannel) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr UniqueChannelId kChannelId0 = l2cap::kFirstDynamicChannelId; |
| constexpr UniqueChannelId kChannelId1 = l2cap::kFirstDynamicChannelId + 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| |
| // Fill up controller buffers |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| RunLoopUntilIdle(); |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| size_t packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ASSERT_LT(sizeof(hci_spec::ACLDataHeader), bytes.size()); |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| |
| // The first packet should be left in the queue because it is for kChannelId0 but the second |
| // should be dropped as it was the least recently sent unsent packet for channel kChannelId1. |
| if (packet_count == 0) { |
| EXPECT_EQ(0u, packet.payload_data()[0]); |
| } else if (packet_count == 1) { |
| EXPECT_EQ(2u, packet.payload_data()[0]); |
| } |
| |
| packet_count++; |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Send one packet on kChannelId0 |
| { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| |
| // Write a sequence number into the payload, starting at 0 |
| packet->mutable_view()->mutable_payload_data()[0] = static_cast<uint8_t>(0); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), kChannelId0, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| // Send enough data on kChannel1 that the first PDU sent in this loop gets dropped |
| for (size_t i = 0; i < AclDataChannel::kMaxAclPacketsPerChannel + 1; i++) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| |
| // Write a sequence number into the payload, starting at 0 |
| packet->mutable_view()->mutable_payload_data()[0] = static_cast<uint8_t>(i + 1); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), kChannelId1, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| test_device()->SendCommandChannelPacket(bt::testing::NumberOfCompletedPacketsPacket(kHandle0, 2)); |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, SendingLowPriorityPacketsThatDropDoNotAffectDataOnDifferentLink) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // Fill up controller buffers |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| RunLoopUntilIdle(); |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| size_t packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ASSERT_LT(sizeof(hci_spec::ACLDataHeader), bytes.size()); |
| PacketView<hci_spec::ACLDataHeader> packet(&bytes, |
| bytes.size() - sizeof(hci_spec::ACLDataHeader)); |
| hci_spec::ConnectionHandle connection_handle = |
| le16toh(packet.header().handle_and_flags) & 0xFFF; |
| |
| // First packet on kHandle0 doesn't get dropped, but first packet on kHandle1 does get dropped |
| // because there are too many queued for that channel on that link. |
| if (packet_count == 0) { |
| EXPECT_EQ(kHandle0, connection_handle); |
| EXPECT_EQ(0u, packet.payload_data()[0]); |
| } else if (packet_count == 1) { |
| EXPECT_EQ(kHandle1, connection_handle); |
| EXPECT_EQ(2u, packet.payload_data()[0]); |
| } |
| packet_count++; |
| }; |
| test_device()->SetDataCallback(data_callback, dispatcher()); |
| |
| // Send one data packet on kHandle0 |
| { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| |
| // Write a sequence number into the payload, starting at 0 |
| packet->mutable_view()->mutable_payload_data()[0] = static_cast<uint8_t>(0); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| // Send enough data on kHandle1 that the first PDU sent in this loop gets dropped |
| for (size_t i = 0; i < AclDataChannel::kMaxAclPacketsPerChannel + 1; i++) { |
| auto packet = ACLDataPacket::New(kHandle1, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| |
| // Write a sequence number into the payload, starting at 0 |
| packet->mutable_view()->mutable_payload_data()[0] = static_cast<uint8_t>(i + 1); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| |
| test_device()->SendCommandChannelPacket(bt::testing::NumberOfCompletedPacketsPacket(kHandle0, 2)); |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, QueuedAclAndLePacketsAreSentUsingSeparateBufferCounts) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets), |
| DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // Fill up both LE and BR/EDR controller buffers, leaving one additional packet in the queue of |
| // each type |
| for (hci_spec::ConnectionHandle handle : {kHandle0, kHandle1}) { |
| for (size_t i = 0; i < kMaxNumPackets + 1; ++i) { |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| } |
| RunLoopUntilIdle(); |
| |
| // Callback invoked by TestDevice when it receive a data packet from us. |
| size_t packet_count = 0; |
| test_device()->SetDataCallback([&](auto&) { packet_count++; }, dispatcher()); |
| |
| StaticByteBuffer event_buffer(0x13, 0x09, // Event header |
| 0x02, // Number of handles |
| LowerBits(kHandle0), UpperBits(kHandle0), LowerBits(uint16_t{1}), |
| UpperBits(uint16_t{1}), // 1 packets on kHandle0 |
| LowerBits(kHandle1), UpperBits(kHandle1), LowerBits(uint16_t{1}), |
| UpperBits(uint16_t{1}) // 1 packets on kHandle1 |
| ); |
| test_device()->SendCommandChannelPacket(event_buffer); |
| RunLoopUntilIdle(); |
| |
| EXPECT_EQ(2u, packet_count); |
| } |
| |
| TEST_F(ACLDataChannelTest, InspectHierarchyContainsOutboundQueueState) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| constexpr hci_spec::ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets), |
| DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL); |
| |
| // Fill up both LE and BR/EDR controller buffers, leaving one additional packet in the queue of |
| // each type |
| for (hci_spec::ConnectionHandle handle : {kHandle0, kHandle1}) { |
| for (size_t i = 0; i < kMaxNumPackets + 1; ++i) { |
| auto packet = ACLDataPacket::New(handle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| EXPECT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| } |
| RunLoopUntilIdle(); |
| |
| inspect::Inspector inspector; |
| const std::string kNodeName = "adc_node_name"; |
| acl_data_channel()->AttachInspect(inspector.GetRoot(), kNodeName); |
| |
| using namespace ::inspect::testing; |
| auto bredr_matcher = NodeMatches(AllOf( |
| NameMatches("bredr"), PropertyList(ElementsAre(UintIs("num_sent_packets", kMaxNumPackets))))); |
| |
| auto le_matcher = NodeMatches( |
| AllOf(NameMatches("le"), |
| PropertyList(UnorderedElementsAre(UintIs("num_sent_packets", kMaxNumPackets), |
| BoolIs("independent_from_bredr", true))))); |
| |
| auto send_latency_matcher = |
| NodeMatches(AllOf(NameMatches("send_latency"), |
| PropertyList(UnorderedElementsAre(IntIs("50th_percentile_us", 0), |
| IntIs("95th_percentile_us", 0), |
| IntIs("99th_percentile_us", 0))))); |
| |
| auto send_size_matcher = |
| NodeMatches(AllOf(NameMatches("send_size"), |
| PropertyList(UnorderedElementsAre(UintIs("10th_percentile_bytes", 0), |
| UintIs("50th_percentile_bytes", 0), |
| UintIs("90th_percentile_bytes", 0))))); |
| |
| auto metrics_matcher = |
| AllOf(NodeMatches(NameMatches("metrics")), |
| ChildrenMatch(UnorderedElementsAre(send_latency_matcher, send_size_matcher))); |
| |
| auto adc_matcher = AllOf( |
| NodeMatches(AllOf(NameMatches(kNodeName), |
| PropertyList(UnorderedElementsAre( |
| UintIs("num_queued_packets", 2), UintIs("num_overflow_packets", 0), |
| UintIs("num_recent_overflow_packets", 0))))), |
| ChildrenMatch(UnorderedElementsAre(bredr_matcher, le_matcher, metrics_matcher))); |
| |
| auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo()).take_value(); |
| EXPECT_THAT(hierarchy, ChildrenMatch(ElementsAre(adc_matcher))); |
| } |
| |
| TEST_F(ACLDataChannelTest, SendingPacketsUpdatesSendMetrics) { |
| constexpr size_t kMaxMtu = 4; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr hci_spec::ConnectionHandle kHandle0 = 0x0001; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMtu, kMaxNumPackets)); |
| |
| inspect::Inspector inspector; |
| acl_data_channel()->AttachInspect(inspector.GetRoot(), AclDataChannel::kInspectNodeName); |
| |
| acl_data_channel()->RegisterLink(kHandle0, bt::LinkType::kACL); |
| |
| // Fill up controller buffers |
| for (size_t i = 0; i < kMaxNumPackets; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| ASSERT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| } |
| RunLoopUntilIdle(); |
| |
| // Send enough data that metrics are exported |
| constexpr size_t kNumTestPackets = 256; |
| constexpr zx::duration kSendLatency = zx::msec(1); |
| for (size_t i = 0; i < kNumTestPackets; ++i) { |
| auto packet = ACLDataPacket::New(kHandle0, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable, |
| hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMtu); |
| ASSERT_TRUE(acl_data_channel()->SendPacket(std::move(packet), l2cap::kFirstDynamicChannelId, |
| AclDataChannel::PacketPriority::kLow)); |
| RunLoopFor(kSendLatency); |
| test_device()->SendCommandChannelPacket( |
| bt::testing::NumberOfCompletedPacketsPacket(kHandle0, 1)); |
| RunLoopUntilIdle(); |
| } |
| |
| // Wait for the metric writing task to catch up |
| RunLoopRepeatedlyFor(zx::min(1)); |
| |
| inspect::Hierarchy hierarchy = ReadInspect(inspector); |
| const inspect::Hierarchy* const send_latency_node = |
| hierarchy.GetByPath({AclDataChannel::kInspectNodeName, "metrics", "send_latency"}); |
| ASSERT_TRUE(send_latency_node); |
| const auto* const send_latency_median_wrapped = |
| send_latency_node->node().get_property<inspect::IntPropertyValue>("50th_percentile_us"); |
| ASSERT_TRUE(send_latency_median_wrapped); |
| EXPECT_EQ(kSendLatency.to_usecs(), send_latency_median_wrapped->value()); |
| |
| const inspect::Hierarchy* const send_size_node = |
| hierarchy.GetByPath({AclDataChannel::kInspectNodeName, "metrics", "send_size"}); |
| ASSERT_TRUE(send_size_node); |
| const auto* const send_size_median_wrapped = |
| send_size_node->node().get_property<inspect::UintPropertyValue>("50th_percentile_bytes"); |
| ASSERT_TRUE(send_size_median_wrapped); |
| EXPECT_EQ(kMaxMtu, send_size_median_wrapped->value()); |
| } |
| |
| } // namespace |
| } // namespace bt::hci |