| // 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/hci/acl_data_channel.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <zircon/assert.h> |
| |
| #include <unordered_map> |
| |
| #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/hci/transport.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" |
| |
| namespace bt::hci { |
| namespace { |
| |
| constexpr hci::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(HCI_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(HCI_ACLDataChannelTest, SendPacketBREDRBuffer) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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 using the LE buffer when no BR/EDR buffer is |
| // available. |
| TEST_F(HCI_ACLDataChannelTest, SendPacketLEBuffer) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kTotalAttempts = 12; |
| constexpr size_t kTotalExpected = 6; |
| constexpr size_t kBufferNumPackets = 3; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(), DataBufferInfo(kMaxMTU, kBufferNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kACL); |
| |
| // This should fail because the payload exceeds the MTU. |
| auto packet = ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, SendLEPacketBothBuffers) { |
| constexpr size_t kMaxMTU = 200; |
| constexpr size_t kMaxNumPackets = 50; |
| constexpr size_t kLEMaxMTU = 5; |
| constexpr size_t kLEMaxNumPackets = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), |
| DataBufferInfo(kLEMaxMTU, kLEMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kLE); |
| |
| // This should fail because the payload exceeds the LE MTU. |
| auto packet = ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, SendBREDRPacketBothBuffers) { |
| constexpr size_t kLEMaxMTU = 200; |
| constexpr size_t kLEMaxNumPackets = 50; |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), |
| DataBufferInfo(kLEMaxMTU, kLEMaxNumPackets)); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kACL); |
| |
| // This should fail because the payload exceeds the ACL MTU. |
| auto packet = ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, SendPacketsFailure) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr ConnectionHandle kHandle = 0x0001; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, Connection::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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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 HCI_ACLDataChannelDeathTest = HCI_ACLDataChannelTest; |
| |
| TEST_F(HCI_ACLDataChannelDeathTest, SendPacketsCrashesWithContinuingFragments) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr ConnectionHandle kHandle = 0x0001; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, Connection::LinkType::kACL); |
| |
| LinkedList<ACLDataPacket> packets; |
| packets.push_back(ACLDataPacket::New(kHandle, ACLPacketBoundaryFlag::kContinuingFragment, |
| ACLBroadcastFlag::kPointToPoint, kMaxMTU)); |
| ASSERT_DEATH_IF_SUPPORTED( |
| acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId, |
| AclDataChannel::PacketPriority::kLow), |
| "expected full PDU"); |
| } |
| |
| TEST_F(HCI_ACLDataChannelDeathTest, SendPacketsCrashesWithPacketsForMoreThanOneConnection) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kACL); |
| |
| // Packet exceeds MTU |
| LinkedList<ACLDataPacket> packets; |
| packets.push_back(ACLDataPacket::New(kHandle0, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, kMaxMTU)); |
| packets.push_back(ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kContinuingFragment, |
| 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(HCI_ACLDataChannelTest, SendPackets) { |
| constexpr int kExpectedPacketCount = 5; |
| constexpr ConnectionHandle kHandle = 0x0001; |
| |
| InitializeACLDataChannel(DataBufferInfo(1024, 100), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, Connection::LinkType::kLE); |
| |
| bool pass = true; |
| int seq_no = 0; |
| auto data_cb = [&pass, &seq_no](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(ACLDataHeader)); |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| EXPECT_EQ(1u, packet.payload_size()); |
| |
| int cur_no = packet.payload_bytes()[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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(HCI_ACLDataChannelTest, |
| UnregisterLinkDoesNotClearNumSentPacketsAndClearControllerPacketCountDoes) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr ConnectionHandle kHandle1 = 1; |
| constexpr ConnectionHandle kHandle2 = 2; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle2, Connection::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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle2, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(HCI_ACLDataChannelTest, SendingPacketsOnUnregisteredLinkDropsPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 2; |
| constexpr 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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, Connection::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(HCI_ACLDataChannelTest, UnregisterLinkClearsPendingPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr ConnectionHandle kHandle1 = 1; |
| constexpr ConnectionHandle kHandle2 = 2; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle2, Connection::LinkType::kLE); |
| |
| int handle1_packet_count = 0; |
| int handle2_packet_count = 0; |
| auto data_callback = [&](const ByteBuffer& bytes) { |
| ZX_DEBUG_ASSERT(bytes.size() >= sizeof(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle2, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, PacketsQueuedByFlowControlAreNotSentAfterUnregisterLink) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr ConnectionHandle kHandle1 = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle1, Connection::LinkType::kLE); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle1, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, |
| StalePacketsBufferedBeforeFirstUnregisterAndBeforeSecondRegisterAreNotSent) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr ConnectionHandle kHandle = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, Connection::LinkType::kLE); |
| |
| // Unique packet to send to re-registered link with same handle. |
| const auto kPacket = CreateStaticByteBuffer( |
| // 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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, Connection::LinkType::kLE); |
| |
| auto packet = ACLDataPacket::New(kHandle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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(HCI_ACLDataChannelTest, UnregisterLinkDropsFutureSentPackets) { |
| constexpr size_t kMaxMTU = 1024; |
| constexpr size_t kMaxNumPackets = 1; |
| constexpr ConnectionHandle kHandle = 1; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle, Connection::LinkType::kLE); |
| |
| int packet_count = 0; |
| test_device()->SetDataCallback([&](const auto&) { packet_count++; }, dispatcher()); |
| |
| ASSERT_TRUE(acl_data_channel()->SendPacket( |
| ACLDataPacket::New(kHandle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| ACLBroadcastFlag::kPointToPoint, 1), |
| l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow)); |
| |
| RunLoopUntilIdle(); |
| // second packet should not have been sent |
| ASSERT_EQ(1, packet_count); |
| } |
| |
| TEST_F(HCI_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; |
| ConnectionHandle packet0_handle; |
| 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. |
| auto invalid0 = CreateStaticByteBuffer(0x01, 0x00, 0x00); |
| |
| // Malformed packet: the payload size given in the header doesn't match the |
| // actual payload size. |
| auto invalid1 = CreateStaticByteBuffer(0x01, 0x00, 0x02, 0x00, 0x00); |
| |
| // Valid packet on handle 1. |
| auto valid0 = CreateStaticByteBuffer(0x01, 0x00, 0x01, 0x00, 0x00); |
| |
| // Valid packet on handle 2. |
| auto valid1 = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, TransportClosedCallback) { |
| InitializeACLDataChannel(DataBufferInfo(1u, 1u), DataBufferInfo(1u, 1u)); |
| |
| bool closed_cb_called = false; |
| auto closed_cb = [&closed_cb_called] { closed_cb_called = true; }; |
| transport()->SetTransportClosedCallback(closed_cb); |
| |
| async::PostTask(dispatcher(), [this] { test_device()->CloseACLDataChannel(); }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(closed_cb_called); |
| } |
| |
| TEST_F(HCI_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); |
| |
| // We'll send closed events for both channels. The closed callback should get |
| // invoked only once. |
| async::PostTask(dispatcher(), [this] { |
| test_device()->CloseACLDataChannel(); |
| test_device()->CloseCommandChannel(); |
| }); |
| |
| 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(HCI_ACLDataChannelTest, HciEventReceivedAfterShutDown) { |
| InitializeACLDataChannel(DataBufferInfo(1u, 1u), DataBufferInfo(1u, 1u)); |
| |
| // Notify the processed packets with a Number Of Completed Packet HCI event. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, DropQueuedPacketsRemovesPacketsMatchingFilterFromQueue) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| constexpr l2cap::ChannelId kChanId0 = 0x40; |
| constexpr l2cap::ChannelId kChanId1 = 0x41; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kLE); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, HighPriorityPacketsQueuedAfterLowPriorityPacketsAreSentFirst) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 5; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto priority = |
| (i % 2) ? AclDataChannel::PacketPriority::kLow : AclDataChannel::PacketPriority::kHigh; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = CreateStaticByteBuffer(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(HCI_ACLDataChannelTest, OutOfBoundsPacketCountsIgnored) { |
| constexpr size_t kMaxMTU = 5; |
| constexpr size_t kMaxNumPackets = 6; |
| constexpr ConnectionHandle kHandle0 = 0x0001; |
| constexpr ConnectionHandle kHandle1 = 0x0002; |
| |
| InitializeACLDataChannel(DataBufferInfo(kMaxMTU, kMaxNumPackets), DataBufferInfo()); |
| |
| acl_data_channel()->RegisterLink(kHandle0, Connection::LinkType::kACL); |
| acl_data_channel()->RegisterLink(kHandle1, Connection::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(ACLDataHeader)); |
| |
| PacketView<hci::ACLDataHeader> packet(&bytes, bytes.size() - sizeof(ACLDataHeader)); |
| 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) { |
| ConnectionHandle handle = (i % 2) ? kHandle1 : kHandle0; |
| auto packet = ACLDataPacket::New(handle, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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, ACLPacketBoundaryFlag::kFirstNonFlushable, |
| 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. |
| auto event_buffer = |
| CreateStaticByteBuffer(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); |
| |
| auto short_buffer = CreateStaticByteBuffer(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 CommandHeader. |
| const auto op_code = hci::VendorOpCode(0x01); |
| const StaticByteBuffer kEncodedCommand(LowerBits(op_code), UpperBits(op_code), // op code |
| 0x04, // parameter size |
| 0x00, 0x01, 0x02, 0x03); // test parameter |
| |
| constexpr hci::ConnectionHandle kLinkHandle = 0x0001; |
| |
| std::optional<bt_vendor_command_t> encode_vendor_command; |
| std::optional<bt_vendor_params_t> encode_vendor_command_params; |
| set_encode_vendor_command_cb([&](auto command, auto params) { |
| encode_vendor_command = command; |
| encode_vendor_command_params = params; |
| return fit::ok(DynamicByteBuffer(kEncodedCommand)); |
| }); |
| |
| auto cmd_complete = testing::CommandCompletePacket( |
| op_code, kExpectSuccess ? hci::StatusCode::kSuccess : hci::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(encode_vendor_command); |
| EXPECT_EQ(encode_vendor_command.value(), BT_VENDOR_COMMAND_SET_ACL_PRIORITY); |
| ASSERT_TRUE(encode_vendor_command_params); |
| EXPECT_EQ(encode_vendor_command_params->set_acl_priority.connection_handle, kLinkHandle); |
| if (kPriority == hci::AclPriority::kNormal) { |
| EXPECT_EQ(encode_vendor_command_params->set_acl_priority.priority, |
| BT_VENDOR_ACL_PRIORITY_NORMAL); |
| } else { |
| EXPECT_EQ(encode_vendor_command_params->set_acl_priority.priority, BT_VENDOR_ACL_PRIORITY_HIGH); |
| if (kPriority == hci::AclPriority::kSink) { |
| EXPECT_EQ(encode_vendor_command_params->set_acl_priority.direction, |
| BT_VENDOR_ACL_DIRECTION_SINK); |
| } else { |
| EXPECT_EQ(encode_vendor_command_params->set_acl_priority.direction, |
| BT_VENDOR_ACL_DIRECTION_SOURCE); |
| } |
| } |
| } |
| |
| 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(HCI_ACLDataChannelTest, AclPriorityTest, |
| ::testing::ValuesIn(kPriorityParams)); |
| |
| TEST_F(HCI_ACLDataChannelTest, RequestAclPriorityEncodeFails) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| |
| set_encode_vendor_command_cb([&](auto command, auto params) { return fit::error(); }); |
| |
| 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(HCI_ACLDataChannelTest, RequestAclPriorityEncodeReturnsTooSmallBuffer) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| |
| set_encode_vendor_command_cb([&](auto command, auto params) { |
| return fit::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(HCI_ACLDataChannelTest, SetAutomaticFlushTimeout) { |
| const DataBufferInfo kBREDRBufferInfo(1024, 50); |
| InitializeACLDataChannel(kBREDRBufferInfo, DataBufferInfo()); |
| acl_data_channel()->RegisterLink(kLinkHandle, Connection::LinkType::kACL); |
| |
| std::optional<fit::result<void, hci::StatusCode>> cb_status; |
| auto result_cb = [&](auto status) { cb_status = status; }; |
| |
| // Test command complete error |
| const auto kCommandCompleteError = testing::CommandCompletePacket( |
| hci::kWriteAutomaticFlushTimeout, hci::StatusCode::kUnknownConnectionId); |
| EXPECT_CMD_PACKET_OUT(test_device(), 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(cb_status->error(), hci::StatusCode::kUnknownConnectionId); |
| 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(cb_status->error(), hci::StatusCode::kInvalidHCICommandParameters); |
| |
| // Test infinite flush timeout (flush timeout of 0 should be sent). |
| const auto kCommandComplete = |
| testing::CommandCompletePacket(hci::kWriteAutomaticFlushTimeout, hci::StatusCode::kSuccess); |
| EXPECT_CMD_PACKET_OUT(test_device(), testing::WriteAutomaticFlushTimeoutPacket(kLinkHandle, 0), |
| &kCommandComplete); |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(zx::duration::infinite(), kLinkHandle, |
| result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_TRUE(cb_status->is_ok()); |
| cb_status.reset(); |
| |
| // Test msec to parameter conversion (kMaxAutomaticFlushTimeoutDuration(1279) * |
| // conversion_factor(1.6) = 2046). |
| EXPECT_CMD_PACKET_OUT(test_device(), testing::WriteAutomaticFlushTimeoutPacket(kLinkHandle, 2046), |
| &kCommandComplete); |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(kMaxAutomaticFlushTimeoutDuration, kLinkHandle, |
| result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_TRUE(cb_status->is_ok()); |
| cb_status.reset(); |
| |
| // Test too large flush timeout (no command should be sent). |
| acl_data_channel()->SetBrEdrAutomaticFlushTimeout(kMaxAutomaticFlushTimeoutDuration + zx::msec(1), |
| kLinkHandle, result_cb); |
| RunLoopUntilIdle(); |
| ASSERT_TRUE(cb_status.has_value()); |
| EXPECT_TRUE(cb_status->is_error()); |
| EXPECT_EQ(cb_status->error(), hci::StatusCode::kInvalidHCICommandParameters); |
| } |
| |
| } // namespace |
| } // namespace bt::hci |