blob: 29f4f86e8fed5a78dc897deb2f11d8f57d7351c4 [file] [log] [blame]
// 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 "channel_manager.h"
#include <fbl/macros.h>
#include <memory>
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/fake_controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/test_controller.h"
namespace bt {
namespace l2cap {
namespace {
using bt::testing::TestController;
using TestingBase = bt::testing::FakeControllerTest<TestController>;
constexpr hci::ConnectionHandle kTestHandle1 = 0x0001;
constexpr hci::ConnectionHandle kTestHandle2 = 0x0002;
constexpr PSM kTestPsm = 0x0001;
void DoNothing() {}
void NopRxCallback(ByteBufferPtr) {}
void NopLeConnParamCallback(const hci::LEPreferredConnectionParameters&) {}
void NopSecurityCallback(hci::ConnectionHandle, sm::SecurityLevel,
sm::StatusCallback) {}
class L2CAP_ChannelManagerTest : public TestingBase {
public:
L2CAP_ChannelManagerTest() = default;
~L2CAP_ChannelManagerTest() override = default;
void SetUp() override {
SetUp(hci::DataBufferInfo(hci::kMaxACLPayloadSize, 10),
hci::DataBufferInfo());
}
void SetUp(const hci::DataBufferInfo& acl_info,
const hci::DataBufferInfo& le_info) {
TestingBase::SetUp();
TestingBase::InitializeACLDataChannel(acl_info, le_info);
// FakeControllerTest's ACL data callbacks will no longer work after this
// call, as it overwrites ACLDataChannel's data rx handler. This is intended
// as the L2CAP layer takes ownership of ACL data traffic.
chanmgr_ = std::make_unique<ChannelManager>(transport(), dispatcher());
test_device()->StartCmdChannel(test_cmd_chan());
test_device()->StartAclChannel(test_acl_chan());
}
void TearDown() override {
// Don't trigger the data callback when cleaning up the manager.
if (test_device()) {
test_device()->ClearDataCallback();
}
chanmgr_ = nullptr;
TestingBase::TearDown();
}
// Helper functions for registering logical links with default arguments.
void RegisterLE(
hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback lec = DoNothing,
LEConnectionParameterUpdateCallback cpuc = NopLeConnParamCallback,
SecurityUpgradeCallback suc = NopSecurityCallback) {
chanmgr()->RegisterLE(handle, role, std::move(cpuc), std::move(lec),
std::move(suc), dispatcher());
}
void RegisterACL(hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback lec = DoNothing,
SecurityUpgradeCallback suc = NopSecurityCallback) {
chanmgr()->RegisterACL(handle, role, std::move(lec), std::move(suc),
dispatcher());
}
fbl::RefPtr<Channel> ActivateNewFixedChannel(
ChannelId id, hci::ConnectionHandle conn_handle = kTestHandle1,
Channel::ClosedCallback closed_cb = DoNothing,
Channel::RxCallback rx_cb = NopRxCallback) {
auto chan = chanmgr()->OpenFixedChannel(conn_handle, id);
if (!chan ||
!chan->Activate(std::move(rx_cb), std::move(closed_cb), dispatcher())) {
return nullptr;
}
return chan;
}
// |activated_cb| will be called with opened and activated Channel if
// successful and nullptr otherwise.
void ActivateOutboundChannel(PSM psm, ChannelCallback activated_cb,
hci::ConnectionHandle conn_handle = kTestHandle1,
Channel::ClosedCallback closed_cb = DoNothing,
Channel::RxCallback rx_cb = NopRxCallback) {
ChannelCallback open_cb =
[this, activated_cb = std::move(activated_cb), rx_cb = std::move(rx_cb),
closed_cb = std::move(closed_cb)](auto chan) mutable {
if (!chan || !chan->Activate(std::move(rx_cb), std::move(closed_cb),
dispatcher())) {
activated_cb(nullptr);
} else {
activated_cb(std::move(chan));
}
};
chanmgr()->OpenChannel(conn_handle, psm, std::move(open_cb), dispatcher());
}
ChannelManager* chanmgr() const { return chanmgr_.get(); }
private:
std::unique_ptr<ChannelManager> chanmgr_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_ChannelManagerTest);
};
TEST_F(L2CAP_ChannelManagerTest, OpenFixedChannelErrorNoConn) {
// This should fail as the ChannelManager has no entry for |kTestHandle1|.
EXPECT_EQ(nullptr, ActivateNewFixedChannel(kATTChannelId));
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
// This should fail as the ChannelManager has no entry for |kTestHandle2|.
EXPECT_EQ(nullptr, ActivateNewFixedChannel(kATTChannelId, kTestHandle2));
}
TEST_F(L2CAP_ChannelManagerTest, OpenFixedChannelErrorDisallowedId) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
// ACL-U link
RegisterACL(kTestHandle2, hci::Connection::Role::kMaster);
// This should fail as kSMPChannelId is ACL-U only.
EXPECT_EQ(nullptr, ActivateNewFixedChannel(kSMPChannelId, kTestHandle1));
// This should fail as kATTChannelId is LE-U only.
EXPECT_EQ(nullptr, ActivateNewFixedChannel(kATTChannelId, kTestHandle2));
}
TEST_F(L2CAP_ChannelManagerTest, ActivateFailsAfterDeactivate) {
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ASSERT_TRUE(chan);
chan->Deactivate();
// Activate should fail.
EXPECT_FALSE(chan->Activate(NopRxCallback, DoNothing, dispatcher()));
}
TEST_F(L2CAP_ChannelManagerTest, OpenFixedChannelAndUnregisterLink) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1, closed_cb);
ASSERT_TRUE(chan);
EXPECT_EQ(kTestHandle1, chan->link_handle());
// This should notify the channel.
chanmgr()->Unregister(kTestHandle1);
RunLoopUntilIdle();
// |closed_cb| will be called synchronously since it was registered using the
// current thread's task runner.
EXPECT_TRUE(closed_called);
}
TEST_F(L2CAP_ChannelManagerTest, OpenFixedChannelAndCloseChannel) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1, closed_cb);
ASSERT_TRUE(chan);
// Close the channel before unregistering the link. |closed_cb| should not get
// called.
chan->Deactivate();
chanmgr()->Unregister(kTestHandle1);
RunLoopUntilIdle();
EXPECT_FALSE(closed_called);
}
TEST_F(L2CAP_ChannelManagerTest, OpenAndCloseWithLinkMultipleFixedChannels) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
bool att_closed = false;
auto att_closed_cb = [&att_closed] { att_closed = true; };
bool smp_closed = false;
auto smp_closed_cb = [&smp_closed] { smp_closed = true; };
auto att_chan =
ActivateNewFixedChannel(kATTChannelId, kTestHandle1, att_closed_cb);
ASSERT_TRUE(att_chan);
auto smp_chan =
ActivateNewFixedChannel(kLESMPChannelId, kTestHandle1, smp_closed_cb);
ASSERT_TRUE(smp_chan);
smp_chan->Deactivate();
chanmgr()->Unregister(kTestHandle1);
RunLoopUntilIdle();
EXPECT_TRUE(att_closed);
EXPECT_FALSE(smp_closed);
}
TEST_F(L2CAP_ChannelManagerTest, SendingPacketDuringCleanUpHasNoEffect) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
bool data_sent = false;
auto data_cb = [&](const auto& bytes) { data_sent = true; };
test_device()->SetDataCallback(data_cb, dispatcher());
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1, closed_cb);
ASSERT_TRUE(chan);
// Send a packet. This should be posted on the L2CAP dispatcher but not
// processed yet.
EXPECT_TRUE(chan->Send(NewBuffer('h', 'i')));
ASSERT_FALSE(data_sent);
chanmgr()->Unregister(kTestHandle1);
// Once the loop is drained the L2CAP channel should have been notified of
// closure but the package should not get sent.
RunLoopUntilIdle();
EXPECT_TRUE(closed_called);
EXPECT_FALSE(data_sent);
}
// Tests that destroying the ChannelManager cleanly shuts down all channels.
TEST_F(L2CAP_ChannelManagerTest, DestroyingChannelManagerCleansUpChannels) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
bool data_sent = false;
auto data_cb = [&](const auto& bytes) { data_sent = true; };
test_device()->SetDataCallback(data_cb, dispatcher());
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1, closed_cb);
ASSERT_TRUE(chan);
// Send a packet. This should be posted on the L2CAP dispatcher but not
// processed yet.
EXPECT_TRUE(chan->Send(NewBuffer('h', 'i')));
ASSERT_FALSE(data_sent);
TearDown();
// Once the loop is drained the L2CAP channel should have been notified of
// closure but the package should not get sent.
RunLoopUntilIdle();
EXPECT_TRUE(closed_called);
EXPECT_FALSE(data_sent);
}
TEST_F(L2CAP_ChannelManagerTest, DeactivateDoesNotCrashOrHang) {
// Tests that the clean up task posted to the LogicalLink does not crash when
// a dynamic registry is not present (which is the case for LE links).
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ASSERT_TRUE(chan);
chan->Deactivate();
// Loop until the clean up task runs.
RunLoopUntilIdle();
}
TEST_F(L2CAP_ChannelManagerTest,
CallingDeactivateFromClosedCallbackDoesNotCrashOrHang) {
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
auto chan = chanmgr()->OpenFixedChannel(kTestHandle1, kSMPChannelId);
chan->Activate(
NopRxCallback, [chan] { chan->Deactivate(); }, dispatcher());
chanmgr()->Unregister(kTestHandle1); // Triggers ClosedCallback.
RunLoopUntilIdle();
}
TEST_F(L2CAP_ChannelManagerTest, ReceiveData) {
// LE-U link
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
// We use the ATT channel to control incoming packets and the SMP channel to
// quit the message loop.
std::vector<std::string> sdus;
auto att_rx_cb = [&sdus](ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
sdus.push_back(sdu->ToString());
};
bool smp_cb_called = false;
auto smp_rx_cb = [&smp_cb_called](ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
auto att_chan = ActivateNewFixedChannel(
kATTChannelId, kTestHandle1, [] {}, att_rx_cb);
auto smp_chan = ActivateNewFixedChannel(
kLESMPChannelId, kTestHandle1, [] {}, smp_rx_cb);
ASSERT_TRUE(att_chan);
ASSERT_TRUE(smp_chan);
// ATT channel
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x09, 0x00,
// L2CAP B-frame
0x05, 0x00, 0x04, 0x00, 'h', 'e', 'l', 'l', 'o'));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x09, 0x00,
// L2CAP B-frame (partial)
0x0C, 0x00, 0x04, 0x00, 'h', 'o', 'w', ' ', 'a'));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (continuing fragment)
0x01, 0x10, 0x07, 0x00,
// L2CAP B-frame (partial)
'r', 'e', ' ', 'y', 'o', 'u', '?'));
// SMP channel
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame (empty)
0x00, 0x00, 0x06, 0x00));
RunLoopUntilIdle();
EXPECT_TRUE(smp_cb_called);
ASSERT_EQ(2u, sdus.size());
EXPECT_EQ("hello", sdus[0]);
EXPECT_EQ("how are you?", sdus[1]);
}
TEST_F(L2CAP_ChannelManagerTest, ReceiveDataBeforeRegisteringLink) {
constexpr size_t kPacketCount = 10;
StaticByteBuffer<255> buffer;
// We use the ATT channel to control incoming packets and the SMP channel to
// quit the message loop.
size_t packet_count = 0;
auto att_rx_cb = [&packet_count](ByteBufferPtr sdu) { packet_count++; };
bool smp_cb_called = false;
auto smp_rx_cb = [&smp_cb_called](ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
// ATT channel
for (size_t i = 0u; i < kPacketCount; i++) {
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame
0x00, 0x00, 0x04, 0x00));
}
// SMP channel
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame (empty)
0x00, 0x00, 0x06, 0x00));
fbl::RefPtr<Channel> att_chan, smp_chan;
// Run the loop so all packets are received.
RunLoopUntilIdle();
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
att_chan = ActivateNewFixedChannel(
kATTChannelId, kTestHandle1, [] {}, att_rx_cb);
ZX_DEBUG_ASSERT(att_chan);
smp_chan = ActivateNewFixedChannel(
kLESMPChannelId, kTestHandle1, [] {}, smp_rx_cb);
ZX_DEBUG_ASSERT(smp_chan);
RunLoopUntilIdle();
EXPECT_TRUE(smp_cb_called);
EXPECT_EQ(kPacketCount, packet_count);
}
// Receive data after registering the link but before creating the channel.
TEST_F(L2CAP_ChannelManagerTest, ReceiveDataBeforeCreatingChannel) {
constexpr size_t kPacketCount = 10;
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
StaticByteBuffer<255> buffer;
// We use the ATT channel to control incoming packets and the SMP channel to
// quit the message loop.
size_t packet_count = 0;
auto att_rx_cb = [&packet_count](ByteBufferPtr sdu) { packet_count++; };
bool smp_cb_called = false;
auto smp_rx_cb = [&smp_cb_called](ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
// ATT channel
for (size_t i = 0u; i < kPacketCount; i++) {
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame
0x00, 0x00, 0x04, 0x00));
}
// SMP channel
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame (empty)
0x00, 0x00, 0x06, 0x00));
fbl::RefPtr<Channel> att_chan, smp_chan;
// Run the loop so all packets are received.
RunLoopUntilIdle();
att_chan = ActivateNewFixedChannel(
kATTChannelId, kTestHandle1, [] {}, att_rx_cb);
ZX_DEBUG_ASSERT(att_chan);
smp_chan = ActivateNewFixedChannel(
kLESMPChannelId, kTestHandle1, [] {}, smp_rx_cb);
ZX_DEBUG_ASSERT(smp_chan);
RunLoopUntilIdle();
EXPECT_TRUE(smp_cb_called);
EXPECT_EQ(kPacketCount, packet_count);
}
// Receive data after registering the link and creating the channel but before
// setting the rx handler.
TEST_F(L2CAP_ChannelManagerTest, ReceiveDataBeforeSettingRxHandler) {
constexpr size_t kPacketCount = 10;
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto att_chan = chanmgr()->OpenFixedChannel(kTestHandle1, kATTChannelId);
ZX_DEBUG_ASSERT(att_chan);
auto smp_chan = chanmgr()->OpenFixedChannel(kTestHandle1, kLESMPChannelId);
ZX_DEBUG_ASSERT(smp_chan);
StaticByteBuffer<255> buffer;
// We use the ATT channel to control incoming packets and the SMP channel to
// quit the message loop.
size_t packet_count = 0;
auto att_rx_cb = [&packet_count](ByteBufferPtr sdu) { packet_count++; };
bool smp_cb_called = false;
auto smp_rx_cb = [&smp_cb_called](ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
// ATT channel
for (size_t i = 0u; i < kPacketCount; i++) {
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame
0x00, 0x00, 0x04, 0x00));
}
// SMP channel
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (starting fragment)
0x01, 0x00, 0x04, 0x00,
// L2CAP B-frame (empty)
0x00, 0x00, 0x06, 0x00));
// Run the loop so all packets are received.
RunLoopUntilIdle();
att_chan->Activate(att_rx_cb, DoNothing, dispatcher());
smp_chan->Activate(smp_rx_cb, DoNothing, dispatcher());
RunLoopUntilIdle();
EXPECT_TRUE(smp_cb_called);
EXPECT_EQ(kPacketCount, packet_count);
}
TEST_F(L2CAP_ChannelManagerTest, SendOnClosedLink) {
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto att_chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ZX_DEBUG_ASSERT(att_chan);
chanmgr()->Unregister(kTestHandle1);
EXPECT_FALSE(att_chan->Send(NewBuffer('T', 'e', 's', 't')));
}
TEST_F(L2CAP_ChannelManagerTest, SendBasicSdu) {
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto att_chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ZX_DEBUG_ASSERT(att_chan);
std::unique_ptr<ByteBuffer> received;
auto data_cb = [&received](const ByteBuffer& bytes) {
received = std::make_unique<DynamicByteBuffer>(bytes);
};
test_device()->SetDataCallback(data_cb, dispatcher());
EXPECT_TRUE(att_chan->Send(NewBuffer('T', 'e', 's', 't')));
RunLoopUntilIdle();
ASSERT_TRUE(received);
auto expected = CreateStaticByteBuffer(
// ACL data header (handle: 1, length 7)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 3, channel-id: 4)
0x04, 0x00, 0x04, 0x00, 'T', 'e', 's', 't');
EXPECT_TRUE(ContainersEqual(expected, *received));
}
// Tests that fragmentation of LE vs BR/EDR packets is based on the same
// fragment size.
TEST_F(L2CAP_ChannelManagerTest, SendFragmentedSdus) {
constexpr size_t kMaxNumPackets =
100; // Make this large to avoid simulating flow-control.
constexpr size_t kMaxDataSize = 5;
// constexpr size_t kExpectedNumFragments = 5;
// No LE buffers.
TearDown();
SetUp(hci::DataBufferInfo(kMaxDataSize, kMaxNumPackets),
hci::DataBufferInfo());
std::vector<std::unique_ptr<ByteBuffer>> le_fragments, acl_fragments;
auto data_cb = [&le_fragments, &acl_fragments](const ByteBuffer& bytes) {
ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci::ACLDataHeader));
PacketView<hci::ACLDataHeader> packet(
&bytes, bytes.size() - sizeof(hci::ACLDataHeader));
hci::ConnectionHandle handle =
le16toh(packet.header().handle_and_flags) & 0xFFF;
if (handle == kTestHandle1)
le_fragments.push_back(std::make_unique<DynamicByteBuffer>(bytes));
else if (handle == kTestHandle2)
acl_fragments.push_back(std::make_unique<DynamicByteBuffer>(bytes));
};
test_device()->SetDataCallback(data_cb, dispatcher());
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
RegisterACL(kTestHandle2, hci::Connection::Role::kMaster);
// We use the ATT fixed-channel for LE and the SM fixed-channel for ACL.
auto att_chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
auto sm_chan = ActivateNewFixedChannel(kSMPChannelId, kTestHandle2);
ASSERT_TRUE(att_chan);
ASSERT_TRUE(sm_chan);
// SDU of length 5 corresponds to a 9-octet B-frame which should be sent over
// 2 fragments.
EXPECT_TRUE(att_chan->Send(NewBuffer('H', 'e', 'l', 'l', 'o')));
// SDU of length 7 corresponds to a 11-octet B-frame which should be sent over
// 3 fragments.
EXPECT_TRUE(sm_chan->Send(NewBuffer('G', 'o', 'o', 'd', 'b', 'y', 'e')));
RunLoopUntilIdle();
EXPECT_EQ(2u, le_fragments.size());
ASSERT_EQ(3u, acl_fragments.size());
auto expected_le_0 = CreateStaticByteBuffer(
// ACL data header (handle: 1, length: 5)
0x01, 0x00, 0x05, 0x00,
// L2CAP B-frame: (length: 5, channel-id: 4, partial payload)
0x05, 0x00, 0x04, 0x00, 'H');
auto expected_le_1 = CreateStaticByteBuffer(
// ACL data header (handle: 1, pbf: continuing fr., length: 4)
0x01, 0x10, 0x04, 0x00,
// Continuing payload
'e', 'l', 'l', 'o');
auto expected_acl_0 = CreateStaticByteBuffer(
// ACL data header (handle: 2, length: 5)
0x02, 0x00, 0x05, 0x00,
// l2cap b-frame: (length: 7, channel-id: 7, partial payload)
0x07, 0x00, 0x07, 0x00, 'G');
auto expected_acl_1 = CreateStaticByteBuffer(
// ACL data header (handle: 2, pbf: continuing fr., length: 5)
0x02, 0x10, 0x05, 0x00,
// continuing payload
'o', 'o', 'd', 'b', 'y');
auto expected_acl_2 = CreateStaticByteBuffer(
// ACL data header (handle: 2, pbf: continuing fr., length: 1)
0x02, 0x10, 0x01, 0x00,
// Continuing payload
'e');
EXPECT_TRUE(ContainersEqual(expected_le_0, *le_fragments[0]));
EXPECT_TRUE(ContainersEqual(expected_le_1, *le_fragments[1]));
EXPECT_TRUE(ContainersEqual(expected_acl_0, *acl_fragments[0]));
EXPECT_TRUE(ContainersEqual(expected_acl_1, *acl_fragments[1]));
EXPECT_TRUE(ContainersEqual(expected_acl_2, *acl_fragments[2]));
}
// Tests that fragmentation of LE and BR/EDR packets use the corresponding
// buffer size.
TEST_F(L2CAP_ChannelManagerTest, SendFragmentedSdusDifferentBuffers) {
constexpr size_t kMaxNumPackets =
100; // This is large to avoid having to simulate flow-control
constexpr size_t kMaxACLDataSize = 6;
constexpr size_t kMaxLEDataSize = 10;
// constexpr size_t kExpectedNumFragments = 3;
TearDown();
SetUp(hci::DataBufferInfo(kMaxACLDataSize, kMaxNumPackets),
hci::DataBufferInfo(kMaxLEDataSize, kMaxNumPackets));
std::vector<std::unique_ptr<ByteBuffer>> le_fragments, acl_fragments;
auto data_cb = [&le_fragments, &acl_fragments](const ByteBuffer& bytes) {
ZX_DEBUG_ASSERT(bytes.size() >= sizeof(hci::ACLDataHeader));
PacketView<hci::ACLDataHeader> packet(
&bytes, bytes.size() - sizeof(hci::ACLDataHeader));
hci::ConnectionHandle handle =
le16toh(packet.header().handle_and_flags) & 0xFFF;
if (handle == kTestHandle1)
le_fragments.push_back(std::make_unique<DynamicByteBuffer>(bytes));
else if (handle == kTestHandle2)
acl_fragments.push_back(std::make_unique<DynamicByteBuffer>(bytes));
};
test_device()->SetDataCallback(data_cb, dispatcher());
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
RegisterACL(kTestHandle2, hci::Connection::Role::kMaster);
// We use the ATT fixed-channel for LE and the SM fixed-channel for ACL.
auto att_chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
auto sm_chan = ActivateNewFixedChannel(kSMPChannelId, kTestHandle2);
ASSERT_TRUE(att_chan);
ASSERT_TRUE(sm_chan);
// SDU of length 5 corresponds to a 9-octet B-frame. The LE buffer size is
// large enough for this to be sent over a single fragment.
EXPECT_TRUE(att_chan->Send(NewBuffer('H', 'e', 'l', 'l', 'o')));
// SDU of length 7 corresponds to a 11-octet B-frame. Due to the BR/EDR buffer
// size, this should be sent over 2 fragments.
EXPECT_TRUE(sm_chan->Send(NewBuffer('G', 'o', 'o', 'd', 'b', 'y', 'e')));
RunLoopUntilIdle();
EXPECT_EQ(1u, le_fragments.size());
ASSERT_EQ(2u, acl_fragments.size());
auto expected_le = CreateStaticByteBuffer(
// ACL data header (handle: 1, length: 9)
0x01, 0x00, 0x09, 0x00,
// L2CAP B-frame: (length: 5, channel-id: 4)
0x05, 0x00, 0x04, 0x00, 'H', 'e', 'l', 'l', 'o');
auto expected_acl_0 = CreateStaticByteBuffer(
// ACL data header (handle: 2, length: 6)
0x02, 0x00, 0x06, 0x00,
// l2cap b-frame: (length: 7, channel-id: 7, partial payload)
0x07, 0x00, 0x07, 0x00, 'G', 'o');
auto expected_acl_1 = CreateStaticByteBuffer(
// ACL data header (handle: 2, pbf: continuing fr., length: 5)
0x02, 0x10, 0x05, 0x00,
// continuing payload
'o', 'd', 'b', 'y', 'e');
EXPECT_TRUE(ContainersEqual(expected_le, *le_fragments[0]));
EXPECT_TRUE(ContainersEqual(expected_acl_0, *acl_fragments[0]));
EXPECT_TRUE(ContainersEqual(expected_acl_1, *acl_fragments[1]));
}
TEST_F(L2CAP_ChannelManagerTest, LEChannelSignalLinkError) {
bool link_error = false;
auto link_error_cb = [&link_error] { link_error = true; };
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster, link_error_cb);
// Activate a new Attribute channel to signal the error.
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
chan->SignalLinkError();
// The event will run asynchronously.
EXPECT_FALSE(link_error);
RunLoopUntilIdle();
EXPECT_TRUE(link_error);
}
TEST_F(L2CAP_ChannelManagerTest, ACLChannelSignalLinkError) {
bool link_error = false;
auto link_error_cb = [&link_error] { link_error = true; };
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster, link_error_cb);
// Activate a new Security Manager channel to signal the error.
auto chan = ActivateNewFixedChannel(kSMPChannelId, kTestHandle1);
chan->SignalLinkError();
// The event will run asynchronously.
EXPECT_FALSE(link_error);
RunLoopUntilIdle();
EXPECT_TRUE(link_error);
}
TEST_F(L2CAP_ChannelManagerTest, LEConnectionParameterUpdateRequest) {
bool conn_param_cb_called = false;
auto conn_param_cb = [&conn_param_cb_called](const auto& params) {
// The parameters should match the payload of the HCI packet seen below.
EXPECT_EQ(0x0006, params.min_interval());
EXPECT_EQ(0x0C80, params.max_interval());
EXPECT_EQ(0x01F3, params.max_latency());
EXPECT_EQ(0x0C80, params.supervision_timeout());
conn_param_cb_called = true;
};
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster, DoNothing,
conn_param_cb);
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0005 (LE sig))
0x0C, 0x00, 0x05, 0x00,
// L2CAP C-frame header
// (LE conn. param. update request, id: 1, length: 8 bytes)
0x12, 0x01, 0x08, 0x00,
// Connection parameters (hardcoded to match the expections in
// |conn_param_cb|).
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C));
// clang-format on
RunLoopUntilIdle();
EXPECT_TRUE(conn_param_cb_called);
}
TEST_F(L2CAP_ChannelManagerTest, ACLOutboundDynamicChannelLocalDisconnect) {
constexpr ChannelId kLocalId = 0x0040;
constexpr ChannelId kRemoteId = 0x9042;
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
fbl::RefPtr<Channel> channel;
auto channel_cb = [&channel](fbl::RefPtr<l2cap::Channel> activated_chan) {
channel = std::move(activated_chan);
};
bool closed_cb_called = false;
auto closed_cb = [&closed_cb_called] { closed_cb_called = true; };
ActivateOutboundChannel(kTestPsm, std::move(channel_cb), kTestHandle1,
std::move(closed_cb));
RunLoopUntilIdle();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Connection Response (ID: 1, length: 8, dst cid: 0x9042,
// src cid: 0x0040, result: success, status: none)
0x03, 0x01, 0x08, 0x00,
0x42, 0x90, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, dst cid: 0x0040, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01, 0x00, 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 2, length: 6, src cid: 0x0040, flags: 0,
// result: success)
0x05, 0x02, 0x06, 0x00,
0x40, 0x00, 0x00, 0x00,
0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
ASSERT_TRUE(channel);
EXPECT_FALSE(closed_cb_called);
EXPECT_EQ(kLocalId, channel->id());
EXPECT_EQ(kRemoteId, channel->remote_id());
// Test SDU transmission.
std::unique_ptr<ByteBuffer> received;
auto data_cb = [&received](const ByteBuffer& bytes) {
received = std::make_unique<DynamicByteBuffer>(bytes);
};
test_device()->SetDataCallback(std::move(data_cb), dispatcher());
EXPECT_TRUE(channel->Send(NewBuffer('T', 'e', 's', 't')));
RunLoopUntilIdle();
ASSERT_TRUE(received);
// SDU must have remote channel ID (unlike for fixed channels).
auto expected = CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x9042)
0x04, 0x00, 0x42, 0x90, 'T', 'e', 's', 't');
EXPECT_TRUE(ContainersEqual(expected, *received));
// Explicit deactivation should not result in |closed_cb| being called.
channel->Deactivate();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Disconnection Response
// (ID: 3, length: 4, dst cid: 0x9042, src cid: 0x0040)
0x07, 0x03, 0x04, 0x00,
0x42, 0x90, 0x40, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_FALSE(closed_cb_called);
}
TEST_F(L2CAP_ChannelManagerTest, ACLOutboundDynamicChannelRemoteDisconnect) {
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
fbl::RefPtr<Channel> channel;
auto channel_cb = [&channel](fbl::RefPtr<l2cap::Channel> activated_chan) {
channel = std::move(activated_chan);
};
bool channel_closed = false;
auto closed_cb = [&channel_closed] { channel_closed = true; };
bool sdu_received = false;
auto data_rx_cb = [&sdu_received](ByteBufferPtr sdu) {
sdu_received = true;
ZX_DEBUG_ASSERT(sdu);
EXPECT_EQ("Test", sdu->AsString());
};
ActivateOutboundChannel(kTestPsm, std::move(channel_cb), kTestHandle1,
std::move(closed_cb), std::move(data_rx_cb));
RunLoopUntilIdle();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Connection Response (ID: 1, length: 8, dst cid: 0x0047,
// src cid: 0x0040, result: success, status: none)
0x03, 0x01, 0x08, 0x00,
0x47, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, dst cid: 0x0040, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01, 0x00, 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 2, length: 6, src cid: 0x0040, flags: 0,
// result: success)
0x05, 0x02, 0x06, 0x00,
0x40, 0x00, 0x00, 0x00,
0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_NE(nullptr, channel);
EXPECT_FALSE(channel_closed);
// Test SDU reception.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040)
0x04, 0x00, 0x40, 0x00, 'T', 'e', 's', 't'));
RunLoopUntilIdle();
EXPECT_TRUE(sdu_received);
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Disconnection Request
// (ID: 7, length: 4, dst cid: 0x0040, src cid: 0x0047)
0x06, 0x07, 0x04, 0x00,
0x40, 0x00, 0x47, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_TRUE(channel_closed);
}
TEST_F(L2CAP_ChannelManagerTest, ACLOutboundDynamicChannelDataNotBuffered) {
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
fbl::RefPtr<Channel> channel;
auto channel_cb = [&channel](fbl::RefPtr<l2cap::Channel> activated_chan) {
channel = std::move(activated_chan);
};
bool channel_closed = false;
auto closed_cb = [&channel_closed] { channel_closed = true; };
auto data_rx_cb = [](ByteBufferPtr sdu) {
FAIL() << "Unexpected data reception";
};
// Receive SDU for the channel about to be opened. It should be ignored.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040)
0x04, 0x00, 0x40, 0x00, 'T', 'e', 's', 't'));
ActivateOutboundChannel(kTestPsm, std::move(channel_cb), kTestHandle1,
std::move(closed_cb), std::move(data_rx_cb));
RunLoopUntilIdle();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Connection Response (ID: 1, length: 8, dst cid: 0x0047,
// src cid: 0x0040, result: success, status: none)
0x03, 0x01, 0x08, 0x00,
0x47, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00));
// The channel is connected but not configured, so no data should flow on the
// channel. Test that this received data is also ignored.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040)
0x04, 0x00, 0x40, 0x00, 'T', 'e', 's', 't'));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, dst cid: 0x0040, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01, 0x00, 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 2, length: 6, src cid: 0x0040, flags: 0,
// result: success)
0x05, 0x02, 0x06, 0x00,
0x40, 0x00, 0x00, 0x00,
0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_NE(nullptr, channel);
EXPECT_FALSE(channel_closed);
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Disconnection Request
// (ID: 7, length: 4, dst cid: 0x0040, src cid: 0x0047)
0x06, 0x07, 0x04, 0x00,
0x40, 0x00, 0x47, 0x00));
// clang-format on
RunLoopUntilIdle();
}
TEST_F(L2CAP_ChannelManagerTest, ACLOutboundDynamicChannelRemoteRefused) {
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
bool channel_cb_called = false;
auto channel_cb = [&channel_cb_called](fbl::RefPtr<l2cap::Channel> channel) {
channel_cb_called = true;
EXPECT_FALSE(channel);
};
ActivateOutboundChannel(kTestPsm, std::move(channel_cb));
RunLoopUntilIdle();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Connection Response (ID: 1, length: 8, dst cid: 0x0000 (invalid),
// src cid: 0x0040, result: 0x0004 (Refused; no resources available),
// status: none)
0x03, 0x01, 0x08, 0x00,
0x00, 0x00, 0x40, 0x00,
0x04, 0x00, 0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_TRUE(channel_cb_called);
}
TEST_F(L2CAP_ChannelManagerTest, ACLOutboundDynamicChannelFailedConfiguration) {
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
bool channel_cb_called = false;
auto channel_cb = [&channel_cb_called](fbl::RefPtr<l2cap::Channel> channel) {
channel_cb_called = true;
EXPECT_FALSE(channel);
};
ActivateOutboundChannel(kTestPsm, std::move(channel_cb));
RunLoopUntilIdle();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Connection Response (ID: 1, length: 8, dst cid: 0x0047,
// src cid: 0x0040, result: success, status: none)
0x03, 0x01, 0x08, 0x00,
0x47, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, dst cid: 0x0040, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01, 0x00, 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 2, length: 6, src cid: 0x0040, flags: 0,
// result: 0x0002 (Rejected; no reason provide))
0x05, 0x02, 0x06, 0x00,
0x40, 0x00, 0x00, 0x00,
0x02, 0x00));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Disconnection Response
// (ID: 3, length: 4, dst cid: 0x9042, src cid: 0x0040)
0x07, 0x03, 0x04, 0x00,
0x47, 0x00, 0x40, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_TRUE(channel_cb_called);
}
TEST_F(L2CAP_ChannelManagerTest, ACLInboundDynamicChannelLocalDisconnect) {
constexpr PSM kBadPsm0 = 0x0004;
constexpr PSM kBadPsm1 = 0x0103;
constexpr ChannelId kLocalId = 0x0040;
constexpr ChannelId kRemoteId = 0x9042;
RegisterACL(kTestHandle1, hci::Connection::Role::kMaster);
bool closed_cb_called = false;
auto closed_cb = [&closed_cb_called] { closed_cb_called = true; };
fbl::RefPtr<Channel> channel;
auto channel_cb = [this, &channel, closed_cb = std::move(closed_cb)](
fbl::RefPtr<l2cap::Channel> opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, DoNothing, dispatcher()));
};
EXPECT_FALSE(chanmgr()->RegisterService(kBadPsm0, channel_cb, dispatcher()));
EXPECT_FALSE(chanmgr()->RegisterService(kBadPsm1, channel_cb, dispatcher()));
EXPECT_TRUE(chanmgr()->RegisterService(kTestPsm, std::move(channel_cb),
dispatcher()));
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Connection Request (ID: 1, length: 4, psm: 0x0001, src cid: 0x9042)
0x02, 0x01, 0x04, 0x00,
0x01, 0x00, 0x42, 0x90));
RunLoopUntilIdle();
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 16 bytes)
0x01, 0x00, 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, dst cid: 0x0040, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
0x40, 0x00, 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01, 0x00, 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 1, length: 6, src cid: 0x0040, flags: 0,
// result: success)
0x05, 0x01, 0x06, 0x00,
0x40, 0x00, 0x00, 0x00,
0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
ASSERT_TRUE(channel);
EXPECT_FALSE(closed_cb_called);
EXPECT_EQ(kLocalId, channel->id());
EXPECT_EQ(kRemoteId, channel->remote_id());
// Test SDU transmission.
std::unique_ptr<ByteBuffer> received;
auto data_cb = [&received](const ByteBuffer& bytes) {
received = std::make_unique<DynamicByteBuffer>(bytes);
};
test_device()->SetDataCallback(std::move(data_cb), dispatcher());
EXPECT_TRUE(channel->Send(NewBuffer('T', 'e', 's', 't')));
RunLoopUntilIdle();
ASSERT_TRUE(received);
// SDU must have remote channel ID (unlike for fixed channels).
auto expected = CreateStaticByteBuffer(
// ACL data header (handle: 1, length 7)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 3, channel-id: 0x9042)
0x04, 0x00, 0x42, 0x90, 'T', 'e', 's', 't');
EXPECT_TRUE(ContainersEqual(expected, *received));
// Explicit deactivation should not result in |closed_cb| being called.
channel->Deactivate();
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 0x0001, length: 12 bytes)
0x01, 0x00, 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Disconnection Response
// (ID: 2, length: 4, dst cid: 0x9042, src cid: 0x0040)
0x07, 0x02, 0x04, 0x00,
0x42, 0x90, 0x40, 0x00));
// clang-format on
RunLoopUntilIdle();
EXPECT_FALSE(closed_cb_called);
}
TEST_F(L2CAP_ChannelManagerTest, LinkSecurityProperties) {
sm::SecurityProperties security(sm::SecurityLevel::kEncrypted, 16, false);
// Has no effect.
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
// Register a link and open a channel. The security properties should be
// accessible using the channel.
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ASSERT_TRUE(chan);
// The channel should start out at the lowest level of security.
EXPECT_EQ(sm::SecurityProperties(), chan->security());
// Assign a new security level.
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
// Channel should return the new security level.
EXPECT_EQ(security, chan->security());
}
// Tests that assigning a new security level on a closed link does nothing.
TEST_F(L2CAP_ChannelManagerTest, AssignLinkSecurityPropertiesOnClosedLink) {
// Register a link and open a channel. The security properties should be
// accessible using the channel.
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster);
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ASSERT_TRUE(chan);
chanmgr()->Unregister(kTestHandle1);
RunLoopUntilIdle();
// Assign a new security level.
sm::SecurityProperties security(sm::SecurityLevel::kEncrypted, 16, false);
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
// Channel should return the old security level.
EXPECT_EQ(sm::SecurityProperties(), chan->security());
}
TEST_F(L2CAP_ChannelManagerTest, UpgradeSecurity) {
// The callback passed to to Channel::UpgradeSecurity().
sm::Status received_status;
int security_status_count = 0;
auto status_callback = [&](sm::Status status) {
received_status = status;
security_status_count++;
};
// The security handler callback assigned when registering a link.
sm::Status delivered_status;
sm::SecurityLevel last_requested_level = sm::SecurityLevel::kNoSecurity;
int security_request_count = 0;
auto security_handler = [&](hci::ConnectionHandle handle,
sm::SecurityLevel level, auto callback) {
EXPECT_EQ(kTestHandle1, handle);
last_requested_level = level;
security_request_count++;
callback(delivered_status);
};
RegisterLE(kTestHandle1, hci::Connection::Role::kMaster, DoNothing,
NopLeConnParamCallback, std::move(security_handler));
auto chan = ActivateNewFixedChannel(kATTChannelId, kTestHandle1);
ASSERT_TRUE(chan);
// Requesting security at or below the current level should succeed without
// doing anything.
chan->UpgradeSecurity(sm::SecurityLevel::kNoSecurity, status_callback);
RunLoopUntilIdle();
EXPECT_EQ(0, security_request_count);
EXPECT_EQ(1, security_status_count);
EXPECT_TRUE(received_status);
// Test reporting an error.
delivered_status = sm::Status(HostError::kNotSupported);
chan->UpgradeSecurity(sm::SecurityLevel::kEncrypted, status_callback);
RunLoopUntilIdle();
EXPECT_EQ(1, security_request_count);
EXPECT_EQ(2, security_status_count);
EXPECT_EQ(delivered_status, received_status);
EXPECT_EQ(sm::SecurityLevel::kEncrypted, last_requested_level);
// Close the link. Future security requests should have no effect.
chanmgr()->Unregister(kTestHandle1);
RunLoopUntilIdle();
chan->UpgradeSecurity(sm::SecurityLevel::kAuthenticated, status_callback);
chan->UpgradeSecurity(sm::SecurityLevel::kAuthenticated, status_callback);
chan->UpgradeSecurity(sm::SecurityLevel::kAuthenticated, status_callback);
RunLoopUntilIdle();
EXPECT_EQ(1, security_request_count);
EXPECT_EQ(2, security_status_count);
}
} // namespace
} // namespace l2cap
} // namespace bt