blob: 6634cf125e7021f6f2b87a5e55fdab9c9665de87 [file] [log] [blame]
// Copyright 2023 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 <gtest/gtest.h>
#include <pw_async_fuchsia/dispatcher.h>
#include "socket_factory.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel_manager_mock_controller_test_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/test_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
namespace bt::socket {
namespace {
using namespace bt::testing;
using TestingBase = l2cap::ChannelManagerMockControllerTest;
// This test harness provides test cases for interactions between SocketFactory and the L2cap layer.
class SocketFactoryL2capIntegrationTest : public TestLoopFixture, public TestingBase {
public:
SocketFactoryL2capIntegrationTest() : TestingBase(dispatcher_), dispatcher_(dispatcher()) {}
~SocketFactoryL2capIntegrationTest() override = default;
protected:
void SetUp() override {
TestingBase::Initialize();
socket_factory_ = std::make_unique<socket::SocketFactory<l2cap::Channel>>();
}
void TearDown() override {
socket_factory_.reset();
TestingBase::DeleteChannelManager();
RunLoopUntilIdle();
DeleteTransport();
}
zx::socket MakeSocketForChannel(l2cap::Channel::WeakPtr channel) {
return socket_factory_->MakeSocketForChannel(std::move(channel));
}
private:
std::unique_ptr<socket::SocketFactory<l2cap::Channel>> socket_factory_;
pw::async::fuchsia::FuchsiaDispatcher dispatcher_;
BT_DISALLOW_COPY_ASSIGN_AND_MOVE(SocketFactoryL2capIntegrationTest);
};
TEST_F(SocketFactoryL2capIntegrationTest, InboundL2capSocket) {
constexpr l2cap::Psm kPsm = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci_spec::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
l2cap::Channel::WeakPtr channel;
auto chan_cb = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
channel = std::move(cb_chan);
};
chanmgr()->RegisterService(kPsm, kChannelParameters, std::move(chan_cb));
RunLoopUntilIdle();
QueueInboundL2capConnection(kLinkHandle, kPsm, kLocalId, kRemoteId);
RunLoopUntilIdle();
ASSERT_TRUE(channel.is_alive());
zx::socket sock = MakeSocketForChannel(channel);
// Test basic channel<->socket interaction by verifying that an ACL packet gets routed to socket
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00,
// L2CAP payload
't', 'e', 's', 't'));
// Run until the packet is written to the socket buffer.
RunLoopUntilIdle();
// Allocate a larger buffer than the number of SDU bytes we expect (which is 4).
StaticByteBuffer<10> socket_bytes;
size_t bytes_read;
zx_status_t status = sock.read(0, socket_bytes.mutable_data(), socket_bytes.size(), &bytes_read);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(4u, bytes_read);
EXPECT_EQ("test", socket_bytes.view(0, bytes_read).AsString());
const char write_data[81] = "🚂🚃🚄🚅🚆🚈🚇🚈🚉🚊🚋🚌🚎🚝🚞🚟🚠🚡🛤🛲";
// Test outbound data fragments using |kMaxDataPacketLength|.
constexpr size_t kFirstFragmentPayloadSize = kMaxDataPacketLength - sizeof(l2cap::BasicHeader);
const StaticByteBuffer<sizeof(hci_spec::ACLDataHeader) + sizeof(l2cap::BasicHeader) +
kFirstFragmentPayloadSize>
kFirstFragment(
// ACL data header (handle: 1, length 64)
0x01, 0x00, 0x40, 0x00,
// L2CAP B-frame: (length: 80, channel-id: 0x9042 (kRemoteId))
0x50, 0x00, 0x42, 0x90,
// L2CAP payload (fragmented)
0xf0, 0x9f, 0x9a, 0x82, 0xf0, 0x9f, 0x9a, 0x83, 0xf0, 0x9f, 0x9a, 0x84, 0xf0, 0x9f, 0x9a,
0x85, 0xf0, 0x9f, 0x9a, 0x86, 0xf0, 0x9f, 0x9a, 0x88, 0xf0, 0x9f, 0x9a, 0x87, 0xf0, 0x9f,
0x9a, 0x88, 0xf0, 0x9f, 0x9a, 0x89, 0xf0, 0x9f, 0x9a, 0x8a, 0xf0, 0x9f, 0x9a, 0x8b, 0xf0,
0x9f, 0x9a, 0x8c, 0xf0, 0x9f, 0x9a, 0x8e, 0xf0, 0x9f, 0x9a, 0x9d, 0xf0, 0x9f, 0x9a, 0x9e);
constexpr size_t kSecondFragmentPayloadSize = sizeof(write_data) - 1 - kFirstFragmentPayloadSize;
const StaticByteBuffer<sizeof(hci_spec::ACLDataHeader) + kSecondFragmentPayloadSize>
kSecondFragment(
// ACL data header (handle: 1, pbf: continuing fr., length: 20)
0x01, 0x10, 0x14, 0x00,
// L2CAP payload (final fragment)
0xf0, 0x9f, 0x9a, 0x9f, 0xf0, 0x9f, 0x9a, 0xa0, 0xf0, 0x9f, 0x9a, 0xa1, 0xf0, 0x9f, 0x9b,
0xa4, 0xf0, 0x9f, 0x9b, 0xb2);
// The 80-byte write should be fragmented over 64- and 20-byte HCI payloads in order to send it
// to the controller.
EXPECT_ACL_PACKET_OUT(test_device(), kFirstFragment);
EXPECT_ACL_PACKET_OUT(test_device(), kSecondFragment);
size_t bytes_written = 0;
// Write 80 outbound bytes to the socket buffer.
status = sock.write(0, write_data, sizeof(write_data) - 1, &bytes_written);
EXPECT_EQ(ZX_OK, status);
EXPECT_EQ(80u, bytes_written);
// Run until the data is flushed out to the MockController.
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
// Synchronously closes channels & sockets.
chanmgr()->RemoveConnection(kLinkHandle);
acl_data_channel()->UnregisterConnection(kLinkHandle);
acl_data_channel()->ClearControllerPacketCount(kLinkHandle);
// try resending data now that connection is closed
bytes_written = 0;
status = sock.write(0, write_data, sizeof(write_data) - 1, &bytes_written);
EXPECT_EQ(ZX_ERR_PEER_CLOSED, status);
EXPECT_EQ(0u, bytes_written);
// no packets should be sent
RunLoopUntilIdle();
}
TEST_F(SocketFactoryL2capIntegrationTest, OutboundL2capSocket) {
constexpr l2cap::Psm kPsm = l2cap::kAVCTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci_spec::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
l2cap::Channel::WeakPtr chan;
auto chan_cb = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
chan = std::move(cb_chan);
};
QueueOutboundL2capConnection(kLinkHandle, kPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
// We should have opened a channel successfully.
ASSERT_TRUE(chan.is_alive());
zx::socket sock = MakeSocketForChannel(chan);
// Test basic channel<->socket interaction by verifying that an ACL packet gets routed to socket.
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00,
// L2CAP payload
't', 'e', 's', 't'));
// Run until the packet is written to the socket buffer.
RunLoopUntilIdle();
// Allocate a larger buffer than the number of SDU bytes we expect (which is 4).
StaticByteBuffer<10> socket_bytes;
size_t bytes_read;
zx_status_t status = sock.read(0, socket_bytes.mutable_data(), socket_bytes.size(), &bytes_read);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(4u, bytes_read);
EXPECT_EQ("test", socket_bytes.view(0, bytes_read).AsString());
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclDisconnectionReq(
NextCommandId(), kLinkHandle, kLocalId, kRemoteId));
}
} // namespace
} // namespace bt::socket