blob: 9d4e53acc084e348aa057d282c08c136319a6815 [file] [log] [blame]
// Copyright 2020 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/logical_link.h"
#include "fbl/ref_ptr.h"
#include "lib/fit/single_threaded_executor.h"
#include "lib/gtest/test_loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/att/att.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/mock_acl_data_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/smp.h"
namespace bt::l2cap::internal {
namespace {
using Conn = hci::Connection;
class L2CAP_LogicalLinkTest : public ::gtest::TestLoopFixture {
public:
L2CAP_LogicalLinkTest() = default;
~L2CAP_LogicalLinkTest() override = default;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_LogicalLinkTest);
protected:
void SetUp() override { NewLogicalLink(); }
void TearDown() override {
if (link_) {
link_->Close();
link_ = nullptr;
}
}
void NewLogicalLink(Conn::LinkType type = Conn::LinkType::kLE) {
const hci::ConnectionHandle kConnHandle = 0x0001;
const size_t kMaxPayload = kDefaultMTU;
auto query_service_cb = [](hci::ConnectionHandle, PSM) { return std::nullopt; };
link_ = LogicalLink::New(kConnHandle, type, Conn::Role::kMaster, &executor_, kMaxPayload,
std::move(query_service_cb), &acl_data_channel_,
/*random_channel_ids=*/true);
}
LogicalLink* link() const { return link_.get(); }
void DeleteLink() { link_ = nullptr; }
hci::testing::MockAclDataChannel* acl_data_channel() { return &acl_data_channel_; }
private:
fbl::RefPtr<LogicalLink> link_;
fit::single_threaded_executor executor_;
hci::testing::MockAclDataChannel acl_data_channel_;
};
using L2CAP_LogicalLinkDeathTest = L2CAP_LogicalLinkTest;
TEST_F(L2CAP_LogicalLinkDeathTest, DestructedWithoutClosingDies) {
// Deleting the link without calling `Close` on it should trigger an assertion.
ASSERT_DEATH_IF_SUPPORTED(DeleteLink(), ".*closed.*");
}
TEST_F(L2CAP_LogicalLinkTest, FixedChannelHasCorrectMtu) {
fbl::RefPtr<Channel> fixed_chan = link()->OpenFixedChannel(kATTChannelId);
ASSERT_TRUE(fixed_chan);
EXPECT_EQ(kMaxMTU, fixed_chan->max_rx_sdu_size());
EXPECT_EQ(kMaxMTU, fixed_chan->max_tx_sdu_size());
}
TEST_F(L2CAP_LogicalLinkTest, DropsBroadcastPackets) {
link()->Close();
NewLogicalLink(Conn::LinkType::kACL);
fbl::RefPtr<Channel> connectionless_chan = link()->OpenFixedChannel(kConnectionlessChannelId);
ASSERT_TRUE(connectionless_chan);
size_t rx_count = 0;
bool activated = connectionless_chan->Activate([&](ByteBufferPtr) { rx_count++; }, []() {});
ASSERT_TRUE(activated);
auto group_frame = CreateStaticByteBuffer(0x0A, 0x00, // Length (PSM + info = 10)
0x02, 0x00, // Connectionless data channel
0xF0, 0x0F, // PSM
'S', 'a', 'p', 'p', 'h', 'i', 'r', 'e' // Info Payload
);
auto packet =
hci::ACLDataPacket::New(0x0001, hci::ACLPacketBoundaryFlag::kCompletePDU,
hci::ACLBroadcastFlag::kActiveSlaveBroadcast, group_frame.size());
ASSERT_TRUE(packet);
packet->mutable_view()->mutable_payload_data().Write(group_frame);
link()->HandleRxPacket(std::move(packet));
// Should be dropped.
EXPECT_EQ(0u, rx_count);
}
#define EXPECT_HIGH_PRIORITY(channel_id) \
EXPECT_EQ(LogicalLink::ChannelPriority((channel_id)), hci::AclDataChannel::PacketPriority::kHigh)
#define EXPECT_LOW_PRIORITY(channel_id) \
EXPECT_EQ(LogicalLink::ChannelPriority((channel_id)), hci::AclDataChannel::PacketPriority::kLow)
TEST_F(L2CAP_LogicalLinkTest, ChannelPriority) {
EXPECT_HIGH_PRIORITY(kSignalingChannelId);
EXPECT_HIGH_PRIORITY(kLESignalingChannelId);
EXPECT_HIGH_PRIORITY(kSMPChannelId);
EXPECT_HIGH_PRIORITY(kLESMPChannelId);
EXPECT_LOW_PRIORITY(kFirstDynamicChannelId);
EXPECT_LOW_PRIORITY(kLastACLDynamicChannelId);
EXPECT_LOW_PRIORITY(kATTChannelId);
}
TEST_F(L2CAP_LogicalLinkTest, SetBrEdrAutomaticFlushTimeoutSucceeds) {
link()->Close();
NewLogicalLink(Conn::LinkType::kACL);
constexpr zx::duration kTimeout(zx::msec(100));
acl_data_channel()->set_set_bredr_automatic_flush_timeout_cb(
[&](auto timeout, auto handle, auto cb) {
EXPECT_EQ(timeout, kTimeout);
EXPECT_EQ(handle, link()->handle());
cb(fit::ok());
});
bool cb_called = false;
link()->SetBrEdrAutomaticFlushTimeout(kTimeout, [&](auto result) {
cb_called = true;
EXPECT_TRUE(result.is_ok());
});
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_LogicalLinkTest, SetBrEdrAutomaticFlushTimeoutFailsForLELink) {
constexpr zx::duration kTimeout(zx::msec(100));
// LE links are unsupported, so result should be an error.
link()->Close();
NewLogicalLink(Conn::LinkType::kLE);
// No command should be sent.
acl_data_channel()->set_set_bredr_automatic_flush_timeout_cb(
[&](auto timeout, auto handle, auto cb) { FAIL(); });
bool cb_called = false;
link()->SetBrEdrAutomaticFlushTimeout(kTimeout, [&](auto result) {
cb_called = true;
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), hci::StatusCode::kInvalidHCICommandParameters);
});
EXPECT_TRUE(cb_called);
}
} // namespace
} // namespace bt::l2cap::internal