blob: 98bf5429c9172b300b84af5a6fe0e9a994658bf4 [file] [log] [blame]
// Copyright 2018 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/l2cap/l2cap.h"
#include <lib/async/cpp/task.h>
#include <lib/inspect/testing/cpp/inspect.h>
#include <fbl/macros.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/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/test_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/types.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/status.h"
#include "src/connectivity/bluetooth/core/bt-host/socket/socket_factory.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"
// This test harness provides test cases for interations between L2CAP, RFCOMM,
// and SocketFactory in integration, as they are implemented by the domain
// object. These exercise a production data plane against raw HCI endpoints.
// TODO(fxbug.dev/61914): Migrate these tests to an l2cap integration test suite.
namespace bt::l2cap {
namespace {
using namespace inspect::testing;
using namespace bt::testing;
using bt::testing::MockController;
using TestingBase = bt::testing::ControllerTest<MockController>;
// Sized intentionally to cause fragmentation for outbound dynamic channel data but not others. May
// need adjustment if Signaling Channel Configuration {Request,Response} default transactions get
// much bigger.
constexpr size_t kMaxDataPacketLength = 64;
constexpr size_t kMaxPacketCount = 10;
// 2x Information Requests: Extended Features, Fixed Channels Supported
constexpr size_t kConnectionCreationPacketCount = 2;
constexpr l2cap::ChannelParameters kChannelParameters{l2cap::ChannelMode::kBasic, l2cap::kMaxMTU,
std::nullopt};
constexpr l2cap::ExtendedFeatures kExtendedFeatures =
l2cap::kExtendedFeaturesBitEnhancedRetransmission;
class L2CAP_L2capTest : public TestingBase {
public:
L2CAP_L2capTest() = default;
~L2CAP_L2capTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
const auto bredr_buffer_info = hci::DataBufferInfo(kMaxDataPacketLength, kMaxPacketCount);
InitializeACLDataChannel(bredr_buffer_info);
// TODO(63074): Remove assumptions about channel ordering so we can turn random ids on.
l2cap_ = L2cap::Create(transport()->acl_data_channel(), /*random_channel_ids=*/false);
StartTestDevice();
test_device()->set_data_expectations_enabled(true);
next_command_id_ = 1;
socket_factory_ = std::make_unique<socket::SocketFactory<l2cap::Channel>>();
}
void TearDown() override {
socket_factory_.reset();
l2cap_ = nullptr;
TestingBase::TearDown();
}
l2cap::CommandId NextCommandId() { return next_command_id_++; }
zx::socket MakeSocketForChannel(fbl::RefPtr<l2cap::Channel> channel) {
return socket_factory_->MakeSocketForChannel(channel);
}
void QueueConfigNegotiation(hci::ConnectionHandle handle, l2cap::ChannelParameters local_params,
l2cap::ChannelParameters peer_params, l2cap::ChannelId local_cid,
l2cap::ChannelId remote_cid, l2cap::CommandId local_config_req_id,
l2cap::CommandId peer_config_req_id) {
const auto kPeerConfigRsp =
l2cap::testing::AclConfigRsp(local_config_req_id, handle, local_cid, local_params);
const auto kPeerConfigReq =
l2cap::testing::AclConfigReq(peer_config_req_id, handle, local_cid, peer_params);
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclConfigReq(local_config_req_id, handle, remote_cid, local_params),
&kPeerConfigRsp, &kPeerConfigReq);
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConfigRsp(peer_config_req_id, handle,
remote_cid, peer_params));
}
void QueueInboundL2capConnection(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelId local_cid, l2cap::ChannelId remote_cid,
l2cap::ChannelParameters local_params = kChannelParameters,
l2cap::ChannelParameters peer_params = kChannelParameters) {
const l2cap::CommandId kPeerConnReqId = 1;
const l2cap::CommandId kPeerConfigReqId = kPeerConnReqId + 1;
const auto kConfigReqId = NextCommandId();
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConnectionRsp(kPeerConnReqId, handle,
remote_cid, local_cid));
QueueConfigNegotiation(handle, local_params, peer_params, local_cid, remote_cid, kConfigReqId,
kPeerConfigReqId);
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConnectionReq(kPeerConnReqId, handle, remote_cid, psm));
}
template <typename CallbackT>
void QueueOutboundL2capConnection(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelId local_cid, l2cap::ChannelId remote_cid,
CallbackT open_cb,
l2cap::ChannelParameters local_params = kChannelParameters,
l2cap::ChannelParameters peer_params = kChannelParameters) {
const l2cap::CommandId kPeerConfigReqId = 1;
const auto kConnReqId = NextCommandId();
const auto kConfigReqId = NextCommandId();
const auto kConnRsp =
l2cap::testing::AclConnectionRsp(kConnReqId, handle, local_cid, remote_cid);
EXPECT_ACL_PACKET_OUT(test_device(),
l2cap::testing::AclConnectionReq(kConnReqId, handle, local_cid, psm),
&kConnRsp);
QueueConfigNegotiation(handle, local_params, peer_params, local_cid, remote_cid, kConfigReqId,
kPeerConfigReqId);
l2cap()->OpenL2capChannel(handle, psm, local_params, std::move(open_cb));
}
struct QueueAclConnectionRetVal {
l2cap::CommandId extended_features_id;
l2cap::CommandId fixed_channels_supported_id;
};
QueueAclConnectionRetVal QueueAclConnection(
hci::ConnectionHandle handle, hci::Connection::Role role = hci::Connection::Role::kMaster) {
QueueAclConnectionRetVal cmd_ids;
cmd_ids.extended_features_id = NextCommandId();
cmd_ids.fixed_channels_supported_id = NextCommandId();
const auto kExtFeaturesRsp = l2cap::testing::AclExtFeaturesInfoRsp(cmd_ids.extended_features_id,
handle, kExtendedFeatures);
EXPECT_ACL_PACKET_OUT(
test_device(), l2cap::testing::AclExtFeaturesInfoReq(cmd_ids.extended_features_id, handle),
&kExtFeaturesRsp);
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclFixedChannelsSupportedInfoReq(
cmd_ids.fixed_channels_supported_id, handle));
acl_data_channel()->RegisterLink(handle, hci::Connection::LinkType::kACL);
l2cap()->AddACLConnection(
handle, role, /*link_error_callback=*/[]() {},
/*security_upgrade_callback=*/[](auto, auto, auto) {});
return cmd_ids;
}
L2cap::LEFixedChannels QueueLEConnection(hci::ConnectionHandle handle,
hci::Connection::Role role) {
acl_data_channel()->RegisterLink(handle, hci::Connection::LinkType::kLE);
return l2cap()->AddLEConnection(
handle, role, /*link_error_callback=*/[] {}, /*conn_param_callback=*/[](auto&) {},
/*security_callback=*/[](auto, auto, auto) {});
}
L2cap* l2cap() const { return l2cap_.get(); }
private:
fbl::RefPtr<L2cap> l2cap_;
l2cap::CommandId next_command_id_;
std::unique_ptr<socket::SocketFactory<l2cap::Channel>> socket_factory_;
DISALLOW_COPY_ASSIGN_AND_MOVE(L2CAP_L2capTest);
};
TEST_F(L2CAP_L2capTest, InboundL2capSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
fbl::RefPtr<l2cap::Channel> chan;
auto chan_cb = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
chan = std::move(cb_chan);
};
l2cap()->RegisterService(kPSM, kChannelParameters, std::move(chan_cb));
RunLoopUntilIdle();
QueueInboundL2capConnection(kLinkHandle, kPSM, kLocalId, kRemoteId);
RunLoopUntilIdle();
ASSERT_TRUE(chan);
zx::socket sock = MakeSocketForChannel(chan);
// Test basic channel<->socket interaction by verifying that an ACL packet
// gets routed to the socket.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00, '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] =
u8"🚂🚃🚄🚅🚆🚈🚇🚈🚉🚊🚋🚌🚎🚝🚞🚟🚠🚡🛤🛲";
// Test outbound data fragments using |kMaxDataPacketLength|.
constexpr size_t kFirstFragmentPayloadSize = kMaxDataPacketLength - sizeof(l2cap::BasicHeader);
const auto kFirstFragment =
StaticByteBuffer<sizeof(hci::ACLDataHeader) + sizeof(l2cap::BasicHeader) +
kFirstFragmentPayloadSize>(
// 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 auto kSecondFragment =
StaticByteBuffer<sizeof(hci::ACLDataHeader) + kSecondFragmentPayloadSize>(
// 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.
l2cap()->RemoveConnection(kLinkHandle);
acl_data_channel()->UnregisterLink(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(L2CAP_L2capTest, InboundRfcommSocketFails) {
constexpr l2cap::PSM kPSM = l2cap::kRFCOMM;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
const l2cap::CommandId kPeerConnReqId = 1;
// Incoming connection refused, RFCOMM is not routed.
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclConnectionRsp(kPeerConnReqId, kLinkHandle, kRemoteId, 0x0000 /*dest id*/,
l2cap::ConnectionResult::kPSMNotSupported));
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConnectionReq(kPeerConnReqId, kLinkHandle, kRemoteId, kPSM));
RunLoopUntilIdle();
}
TEST_F(L2CAP_L2capTest, InboundPacketQueuedAfterChannelOpenIsNotDropped) {
constexpr l2cap::PSM kPSM = l2cap::kSDP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
fbl::RefPtr<l2cap::Channel> chan;
auto chan_cb = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
chan = std::move(cb_chan);
};
l2cap()->RegisterService(kPSM, kChannelParameters, std::move(chan_cb));
RunLoopUntilIdle();
constexpr l2cap::CommandId kConnectionReqId = 1;
constexpr l2cap::CommandId kPeerConfigReqId = 6;
const l2cap::CommandId kConfigReqId = NextCommandId();
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConnectionRsp(
kConnectionReqId, kLinkHandle, kRemoteId, kLocalId));
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConfigReq(kConfigReqId, kLinkHandle,
kRemoteId, kChannelParameters));
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConnectionReq(kConnectionReqId, kLinkHandle, kRemoteId, kPSM));
// Config negotiation will not complete yet.
RunLoopUntilIdle();
// Remaining config negotiation will be added to dispatch loop.
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConfigRsp(kPeerConfigReqId, kLinkHandle,
kRemoteId, kChannelParameters));
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConfigReq(kPeerConfigReqId, kLinkHandle, kLocalId, kChannelParameters));
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConfigRsp(kConfigReqId, kLinkHandle, kLocalId, kChannelParameters));
// Queue up a data packet for the new channel before the channel configuration has been
// processed.
ASSERT_FALSE(chan);
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00, 0xf0, 0x9f, 0x94, 0xb0));
// Run until the channel opens and the packet is written to the socket buffer.
RunLoopUntilIdle();
ASSERT_TRUE(chan);
zx::socket sock = MakeSocketForChannel(chan);
// 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(u8"🔰", socket_bytes.view(0, bytes_read).AsString());
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclDisconnectionReq(
NextCommandId(), kLinkHandle, kLocalId, kRemoteId));
}
TEST_F(L2CAP_L2capTest, OutboundL2capSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVCTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
fbl::RefPtr<l2cap::Channel> 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);
zx::socket sock = MakeSocketForChannel(chan);
// Test basic channel<->socket interaction by verifying that an ACL packet
// gets routed to the socket.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00, '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));
}
TEST_F(L2CAP_L2capTest, OutboundChannelIsInvalidWhenL2capFailsToOpenChannel) {
constexpr l2cap::PSM kPSM = l2cap::kAVCTP;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
// Don't register any links. This should cause outbound channels to fail.
bool chan_cb_called = false;
auto chan_cb = [&chan_cb_called](auto chan) {
chan_cb_called = true;
EXPECT_FALSE(chan);
};
l2cap()->OpenL2capChannel(kLinkHandle, kPSM, kChannelParameters, std::move(chan_cb));
RunLoopUntilIdle();
EXPECT_TRUE(chan_cb_called);
}
// Queue dynamic channel packets, then open a new dynamic channel.
// The signaling channel packets should be sent before the queued dynamic channel packets.
TEST_F(L2CAP_L2capTest, ChannelCreationPrioritizedOverDynamicChannelData) {
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
constexpr l2cap::PSM kPSM0 = l2cap::kAVCTP;
constexpr l2cap::ChannelId kLocalId0 = 0x0040;
constexpr l2cap::ChannelId kRemoteId0 = 0x9042;
constexpr l2cap::PSM kPSM1 = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId1 = 0x0041;
constexpr l2cap::ChannelId kRemoteId1 = 0x9043;
// l2cap connection request (or response), config request, config response
constexpr size_t kChannelCreationPacketCount = 3;
QueueAclConnection(kLinkHandle);
fbl::RefPtr<l2cap::Channel> chan0;
auto chan_cb0 = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
chan0 = std::move(cb_chan);
};
l2cap()->RegisterService(kPSM0, kChannelParameters, chan_cb0);
QueueInboundL2capConnection(kLinkHandle, kPSM0, kLocalId0, kRemoteId0);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(chan0);
zx::socket sock0 = MakeSocketForChannel(chan0);
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kLinkHandle, kConnectionCreationPacketCount + kChannelCreationPacketCount));
// Dummy dynamic channel packet
const auto kPacket0 = CreateStaticByteBuffer(
// ACL data header (handle: 1, length 5)
0x01, 0x00, 0x05, 0x00,
// L2CAP B-frame: (length: 1, channel-id: 0x9042 (kRemoteId))
0x01, 0x00, 0x42, 0x90,
// L2CAP payload
0x01);
// |kMaxPacketCount| packets should be sent to the controller,
// and 1 packet should be left in the queue
const char write_data[] = {0x01};
for (size_t i = 0; i < kMaxPacketCount + 1; i++) {
if (i != kMaxPacketCount) {
EXPECT_ACL_PACKET_OUT(test_device(), kPacket0);
}
size_t bytes_written = 0;
zx_status_t status = sock0.write(0, write_data, sizeof(write_data), &bytes_written);
EXPECT_EQ(ZX_OK, status);
EXPECT_EQ(sizeof(write_data), bytes_written);
}
EXPECT_FALSE(test_device()->AllExpectedDataPacketsSent());
// Run until the data is flushed out to the MockController.
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
fbl::RefPtr<l2cap::Channel> chan1;
auto chan_cb1 = [&](auto cb_chan) {
EXPECT_EQ(kLinkHandle, cb_chan->link_handle());
chan1 = std::move(cb_chan);
};
QueueOutboundL2capConnection(kLinkHandle, kPSM1, kLocalId1, kRemoteId1, std::move(chan_cb1));
for (size_t i = 0; i < kChannelCreationPacketCount; i++) {
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(kLinkHandle, 1));
// Wait for next connection creation packet to be queued (eg. configuration request/response).
RunLoopUntilIdle();
}
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(chan1);
// Make room in buffer for queued dynamic channel packet.
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(kLinkHandle, 1));
EXPECT_ACL_PACKET_OUT(test_device(), kPacket0);
RunLoopUntilIdle();
// 1 Queued dynamic channel data packet should have been sent.
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
}
TEST_F(L2CAP_L2capTest, NegotiateChannelParametersOnOutboundL2capSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
constexpr uint16_t kMtu = l2cap::kMinACLMTU;
l2cap::ChannelParameters chan_params;
chan_params.mode = l2cap::ChannelMode::kEnhancedRetransmission;
chan_params.max_rx_sdu_size = kMtu;
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
fbl::RefPtr<l2cap::Channel> chan;
auto chan_cb = [&](fbl::RefPtr<l2cap::Channel> cb_chan) { chan = std::move(cb_chan); };
QueueOutboundL2capConnection(kLinkHandle, kPSM, kLocalId, kRemoteId, chan_cb, chan_params,
chan_params);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(chan);
EXPECT_EQ(kLinkHandle, chan->link_handle());
EXPECT_EQ(*chan_params.max_rx_sdu_size, chan->max_rx_sdu_size());
EXPECT_EQ(*chan_params.mode, chan->mode());
}
TEST_F(L2CAP_L2capTest, NegotiateChannelParametersOnInboundL2capSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
l2cap::ChannelParameters chan_params;
chan_params.mode = l2cap::ChannelMode::kEnhancedRetransmission;
chan_params.max_rx_sdu_size = l2cap::kMinACLMTU;
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
fbl::RefPtr<l2cap::Channel> chan;
auto chan_cb = [&](auto cb_chan) { chan = std::move(cb_chan); };
l2cap()->RegisterService(kPSM, chan_params, chan_cb);
QueueInboundL2capConnection(kLinkHandle, kPSM, kLocalId, kRemoteId, chan_params, chan_params);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(chan);
EXPECT_EQ(*chan_params.max_rx_sdu_size, chan->max_rx_sdu_size());
EXPECT_EQ(*chan_params.mode, chan->mode());
zx::socket sock = MakeSocketForChannel(chan);
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclDisconnectionReq(
NextCommandId(), kLinkHandle, kLocalId, kRemoteId));
}
TEST_F(L2CAP_L2capTest, RequestConnectionParameterUpdateAndReceiveResponse) {
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kSlaveLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
const hci::LEPreferredConnectionParameters kParams(kIntervalMin, kIntervalMax, kSlaveLatency,
kTimeoutMult);
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
QueueLEConnection(kLinkHandle, hci::Connection::Role::kSlave);
std::optional<bool> accepted;
auto request_cb = [&accepted](bool cb_accepted) { accepted = cb_accepted; };
// Receive "Accepted" Response:
l2cap::CommandId param_update_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclConnectionParameterUpdateReq(
param_update_req_id, kLinkHandle, kIntervalMin,
kIntervalMax, kSlaveLatency, kTimeoutMult));
l2cap()->RequestConnectionParameterUpdate(kLinkHandle, kParams, request_cb);
RunLoopUntilIdle();
EXPECT_FALSE(accepted.has_value());
test_device()->SendACLDataChannelPacket(l2cap::testing::AclConnectionParameterUpdateRsp(
param_update_req_id, kLinkHandle, l2cap::ConnectionParameterUpdateResult::kAccepted));
RunLoopUntilIdle();
ASSERT_TRUE(accepted.has_value());
EXPECT_TRUE(accepted.value());
accepted.reset();
}
TEST_F(L2CAP_L2capTest, InspectHierarchy) {
inspect::Inspector inspector;
l2cap()->AttachInspect(inspector.GetRoot(), L2cap::kInspectNodeName);
auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
ASSERT_TRUE(hierarchy);
auto l2cap_matcher =
AllOf(NodeMatches(PropertyList(::testing::IsEmpty())),
ChildrenMatch(UnorderedElementsAre(NodeMatches(NameMatches("logical_links")),
NodeMatches(NameMatches("services")))));
EXPECT_THAT(hierarchy.value(), AllOf(ChildrenMatch(UnorderedElementsAre(l2cap_matcher))));
}
TEST_F(L2CAP_L2capTest, AddLEConnectionReturnsFixedChannels) {
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
auto channels = QueueLEConnection(kLinkHandle, hci::Connection::Role::kSlave);
ASSERT_TRUE(channels.att);
EXPECT_EQ(l2cap::kATTChannelId, channels.att->id());
ASSERT_TRUE(channels.smp);
EXPECT_EQ(l2cap::kLESMPChannelId, channels.smp->id());
}
class AclPriorityTest : public L2CAP_L2capTest,
public ::testing::WithParamInterface<std::pair<hci::AclPriority, bool>> {};
TEST_P(AclPriorityTest, OutboundConnectAndSetPriority) {
const auto kPriority = GetParam().first;
const bool kExpectSuccess = GetParam().second;
// 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 l2cap::PSM kPSM = l2cap::kAVCTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
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));
});
QueueAclConnection(kLinkHandle);
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
fbl::RefPtr<l2cap::Channel> channel = nullptr;
auto chan_cb = [&](auto chan) { channel = std::move(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(channel);
channel->Activate([](auto) {}, []() {});
if (kPriority != hci::AclPriority::kNormal) {
auto cmd_complete = 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;
channel->RequestAclPriority(kPriority, [&](auto result) {
request_cb_count++;
EXPECT_EQ(kExpectSuccess, result.is_ok());
});
RunLoopUntilIdle();
EXPECT_EQ(request_cb_count, 1u);
if (kPriority == hci::AclPriority::kNormal) {
EXPECT_FALSE(encode_vendor_command);
} else {
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);
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);
}
}
encode_vendor_command.reset();
encode_vendor_command_params.reset();
if (kPriority != hci::AclPriority::kNormal && kExpectSuccess) {
auto cmd_complete = CommandCompletePacket(op_code, hci::StatusCode::kSuccess);
EXPECT_CMD_PACKET_OUT(test_device(), kEncodedCommand, &cmd_complete);
}
EXPECT_ACL_PACKET_OUT(test_device(), l2cap::testing::AclDisconnectionReq(
NextCommandId(), kLinkHandle, kLocalId, kRemoteId));
// Deactivating channel should send priority command to revert priority back to normal if it was
// changed.
channel->Deactivate();
RunLoopUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
if (kPriority != hci::AclPriority::kNormal && kExpectSuccess) {
ASSERT_TRUE(encode_vendor_command);
EXPECT_EQ(encode_vendor_command, BT_VENDOR_COMMAND_SET_ACL_PRIORITY);
ASSERT_TRUE(encode_vendor_command_params);
EXPECT_EQ(encode_vendor_command_params->set_acl_priority.connection_handle, kLinkHandle);
EXPECT_EQ(encode_vendor_command_params->set_acl_priority.priority,
BT_VENDOR_ACL_PRIORITY_NORMAL);
} else {
EXPECT_FALSE(encode_vendor_command);
}
}
const std::array<std::pair<hci::AclPriority, bool>, 4> kPriorityParams = {
{{hci::AclPriority::kSource, false},
{hci::AclPriority::kSource, true},
{hci::AclPriority::kSink, true},
{hci::AclPriority::kNormal, true}}};
INSTANTIATE_TEST_SUITE_P(L2CAP_L2capTest, AclPriorityTest, ::testing::ValuesIn(kPriorityParams));
} // namespace
} // namespace bt::l2cap