blob: e087d6b7d30ef9dfa2271d12fd03dca13063db77 [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
#include <memory>
#include <type_traits>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/macros.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/protocol.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_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/inspect.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/acl_data_packet.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/mock_acl_data_channel.h"
#pragma clang diagnostic ignored "-Wshadow"
namespace bt::l2cap {
namespace {
namespace android_hci = bt::hci_spec::vendor::android;
namespace android_emb = pw::bluetooth::vendor::android_hci;
using namespace inspect::testing;
using namespace bt::testing;
using LEFixedChannels = ChannelManager::LEFixedChannels;
using BrEdrFixedChannels = ChannelManager::BrEdrFixedChannels;
using AclPriority = pw::bluetooth::AclPriority;
constexpr hci_spec::ConnectionHandle kTestHandle1 = 0x0001;
constexpr hci_spec::ConnectionHandle kTestHandle2 = 0x0002;
constexpr Psm kTestPsm = 0x0001;
constexpr ChannelId kLocalId = 0x0040;
constexpr ChannelId kRemoteId = 0x9042;
constexpr CommandId kPeerConfigRequestId = 153;
constexpr hci::AclDataChannel::PacketPriority kLowPriority =
hci::AclDataChannel::PacketPriority::kLow;
constexpr hci::AclDataChannel::PacketPriority kHighPriority =
hci::AclDataChannel::PacketPriority::kHigh;
constexpr ChannelParameters kChannelParams;
constexpr pw::chrono::SystemClock::duration kFlushTimeout =
std::chrono::milliseconds(10);
constexpr uint16_t kExpectedFlushTimeoutParam =
16; // 10ms * kFlushTimeoutMsToCommandParameterConversionFactor(1.6)
// 2x Information Requests: Extended Features, Fixed Channels Supported
constexpr size_t kConnectionCreationPacketCount = 2;
void DoNothing() {}
void NopRxCallback(ByteBufferPtr) {}
void NopLeConnParamCallback(const hci_spec::LEPreferredConnectionParameters&) {}
void NopSecurityCallback(hci_spec::ConnectionHandle,
sm::SecurityLevel,
sm::ResultFunction<>) {}
// Holds expected outbound data packets including the source location where the
// expectation is set.
struct PacketExpectation {
const char* file_name;
int line_number;
DynamicByteBuffer data;
bt::LinkType ll_type;
hci::AclDataChannel::PacketPriority priority;
};
// Helpers to set an outbound packet expectation with the link type and source
// location boilerplate prefilled.
// TODO(https://fxbug.dev/42075355): Remove packet priorities from expectations
#define EXPECT_LE_PACKET_OUT(packet_buffer, priority) \
ExpectOutboundPacket( \
bt::LinkType::kLE, (priority), (packet_buffer), __FILE__, __LINE__)
// EXPECT_ACL_PACKET_OUT is already defined by MockController.
#define EXPECT_ACL_PACKET_OUT_(packet_buffer, priority) \
ExpectOutboundPacket( \
bt::LinkType::kACL, (priority), (packet_buffer), __FILE__, __LINE__)
auto MakeExtendedFeaturesInformationRequest(CommandId id,
hci_spec::ConnectionHandle handle) {
return StaticByteBuffer(
// ACL data header (handle, length: 10)
LowerBits(handle),
UpperBits(handle),
0x0a,
0x00,
// L2CAP B-frame header (length: 6, chanel-id: 0x0001 (ACL sig))
0x06,
0x00,
0x01,
0x00,
// Extended Features Information Request (ID, length: 2, type)
0x0a,
id,
0x02,
0x00,
LowerBits(
static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(
static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
}
auto ConfigurationRequest(
CommandId id,
ChannelId dst_id,
uint16_t mtu = kDefaultMTU,
std::optional<RetransmissionAndFlowControlMode> mode = std::nullopt,
uint8_t max_inbound_transmissions = 0) {
if (mode.has_value()) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: 0x0001, length: 27 bytes)
0x01,
0x00,
0x1b,
0x00,
// L2CAP B-frame header (length: 23 bytes, channel-id: 0x0001 (ACL sig))
0x17,
0x00,
0x01,
0x00,
// Configuration Request (ID, length: 19, dst cid, flags: 0)
0x04,
id,
0x13,
0x00,
LowerBits(dst_id),
UpperBits(dst_id),
0x00,
0x00,
// Mtu option (ID, Length, MTU)
0x01,
0x02,
LowerBits(mtu),
UpperBits(mtu),
// Retransmission & Flow Control option (type, length: 9, mode,
// tx_window: 63, max_retransmit: 0, retransmit timeout: 0 ms, monitor
// timeout: 0 ms, mps: 65535)
0x04,
0x09,
static_cast<uint8_t>(*mode),
kErtmMaxUnackedInboundFrames,
max_inbound_transmissions,
0x00,
0x00,
0x00,
0x00,
LowerBits(kMaxInboundPduPayloadSize),
UpperBits(kMaxInboundPduPayloadSize)));
}
return DynamicByteBuffer(StaticByteBuffer(
// 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, length: 8, dst cid, flags: 0)
0x04,
id,
0x08,
0x00,
LowerBits(dst_id),
UpperBits(dst_id),
0x00,
0x00,
// Mtu option (ID, Length, MTU)
0x01,
0x02,
LowerBits(mtu),
UpperBits(mtu)));
}
auto OutboundConnectionResponse(CommandId id) {
return testing::AclConnectionRsp(id, kTestHandle1, kRemoteId, kLocalId);
}
auto InboundConnectionResponse(CommandId id) {
return testing::AclConnectionRsp(id, kTestHandle1, kLocalId, kRemoteId);
}
auto InboundConfigurationRequest(
CommandId id,
uint16_t mtu = kDefaultMTU,
std::optional<RetransmissionAndFlowControlMode> mode = std::nullopt,
uint8_t max_inbound_transmissions = 0) {
return ConfigurationRequest(
id, kLocalId, mtu, mode, max_inbound_transmissions);
}
auto InboundConfigurationResponse(CommandId id) {
return StaticByteBuffer(
// 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, flags: 0, res:
// success)
0x05,
id,
0x06,
0x00,
LowerBits(kLocalId),
UpperBits(kLocalId),
0x00,
0x00,
0x00,
0x00);
}
auto InboundConnectionRequest(CommandId id) {
return StaticByteBuffer(
// 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, length: 4, psm, src cid)
0x02,
id,
0x04,
0x00,
LowerBits(kTestPsm),
UpperBits(kTestPsm),
LowerBits(kRemoteId),
UpperBits(kRemoteId));
}
auto OutboundConnectionRequest(CommandId id) {
return StaticByteBuffer(
// 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, length: 4, psm, src cid)
0x02,
id,
0x04,
0x00,
LowerBits(kTestPsm),
UpperBits(kTestPsm),
LowerBits(kLocalId),
UpperBits(kLocalId));
}
auto OutboundConfigurationRequest(
CommandId id,
uint16_t mtu = kMaxMTU,
std::optional<RetransmissionAndFlowControlMode> mode = std::nullopt) {
return ConfigurationRequest(
id, kRemoteId, mtu, mode, kErtmMaxInboundRetransmissions);
}
// |max_transmissions| is ignored per Core Spec v5.0 Vol 3, Part A, Sec 5.4 but
// still parameterized because this needs to match the value that is sent by our
// L2CAP configuration logic.
auto OutboundConfigurationResponse(
CommandId id,
uint16_t mtu = kDefaultMTU,
std::optional<RetransmissionAndFlowControlMode> mode = std::nullopt,
uint8_t max_transmissions = 0) {
const uint8_t kConfigLength = 10 + (mode.has_value() ? 11 : 0);
const uint16_t kL2capLength = kConfigLength + 4;
const uint16_t kAclLength = kL2capLength + 4;
if (mode.has_value()) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01,
0x00,
LowerBits(kAclLength),
UpperBits(kAclLength),
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
LowerBits(kL2capLength),
UpperBits(kL2capLength),
0x01,
0x00,
// Configuration Response (ID, length, src cid, flags: 0, res: success)
0x05,
id,
kConfigLength,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
0x00,
0x00,
0x00,
0x00,
// MTU option (ID, Length, MTU)
0x01,
0x02,
LowerBits(mtu),
UpperBits(mtu),
// Retransmission & Flow Control option (type, length: 9, mode,
// TxWindow, MaxTransmit, rtx timeout: 2 secs, monitor timeout: 12 secs,
// mps)
0x04,
0x09,
static_cast<uint8_t>(*mode),
kErtmMaxUnackedInboundFrames,
max_transmissions,
LowerBits(kErtmReceiverReadyPollTimerMsecs),
UpperBits(kErtmReceiverReadyPollTimerMsecs),
LowerBits(kErtmMonitorTimerMsecs),
UpperBits(kErtmMonitorTimerMsecs),
LowerBits(kMaxInboundPduPayloadSize),
UpperBits(kMaxInboundPduPayloadSize)));
}
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: 0x0001, length: 14 bytes)
0x01,
0x00,
LowerBits(kAclLength),
UpperBits(kAclLength),
// L2CAP B-frame header (length, channel-id: 0x0001 (ACL sig))
LowerBits(kL2capLength),
UpperBits(kL2capLength),
0x01,
0x00,
// Configuration Response (ID, length, src cid, flags: 0, res: success)
0x05,
id,
kConfigLength,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
0x00,
0x00,
0x00,
0x00,
// MTU option (ID, Length, MTU)
0x01,
0x02,
LowerBits(mtu),
UpperBits(mtu)));
}
auto OutboundDisconnectionRequest(CommandId id) {
return StaticByteBuffer(
// 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, length: 4, dst cid, src cid)
0x06,
id,
0x04,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
LowerBits(kLocalId),
UpperBits(kLocalId));
}
auto OutboundDisconnectionResponse(CommandId id) {
return StaticByteBuffer(
// 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, length: 4, dst cid, src cid)
0x07,
id,
0x04,
0x00,
LowerBits(kLocalId),
UpperBits(kLocalId),
LowerBits(kRemoteId),
UpperBits(kRemoteId));
}
A2dpOffloadManager::Configuration BuildConfiguration(
android_emb::A2dpCodecType codec = android_emb::A2dpCodecType::SBC) {
A2dpOffloadManager::Configuration config;
config.codec = codec;
config.max_latency = 0xFFFF;
config.scms_t_enable.view().enabled().Write(
pw::bluetooth::emboss::GenericEnableParam::DISABLE);
config.scms_t_enable.view().header().Write(0x00);
config.sampling_frequency = android_emb::A2dpSamplingFrequency::HZ_44100;
config.bits_per_sample = android_emb::A2dpBitsPerSample::BITS_PER_SAMPLE_16;
config.channel_mode = android_emb::A2dpChannelMode::MONO;
config.encoded_audio_bit_rate = 0x0;
switch (codec) {
case android_emb::A2dpCodecType::SBC:
config.sbc_configuration.view().block_length().Write(
android_emb::SbcBlockLen::BLOCK_LEN_4);
config.sbc_configuration.view().subbands().Write(
android_emb::SbcSubBands::SUBBANDS_4);
config.sbc_configuration.view().allocation_method().Write(
android_emb::SbcAllocationMethod::SNR);
config.sbc_configuration.view().min_bitpool_value().Write(0x00);
config.sbc_configuration.view().max_bitpool_value().Write(0xFF);
break;
case android_emb::A2dpCodecType::AAC:
config.aac_configuration.view().object_type().Write(0x00);
config.aac_configuration.view().variable_bit_rate().Write(
android_emb::AacEnableVariableBitRate::DISABLE);
break;
case android_emb::A2dpCodecType::LDAC:
config.ldac_configuration.view().vendor_id().Write(
android_hci::kLdacVendorId);
config.ldac_configuration.view().codec_id().Write(
android_hci::kLdacCodecId);
config.ldac_configuration.view().bitrate_index().Write(
android_emb::LdacBitrateIndex::LOW);
config.ldac_configuration.view().ldac_channel_mode().stereo().Write(true);
break;
default:
break;
}
return config;
}
using TestingBase = FakeDispatcherControllerTest<MockController>;
// ChannelManager test fixture that uses MockAclDataChannel to inject inbound
// data and test outbound data. Unexpected outbound packets will cause test
// failures.
class ChannelManagerMockAclChannelTest : public TestingBase {
public:
ChannelManagerMockAclChannelTest() = default;
~ChannelManagerMockAclChannelTest() override = default;
void SetUp() override {
SetUp(hci_spec::kMaxACLPayloadSize, hci_spec::kMaxACLPayloadSize);
}
void SetUp(size_t max_acl_payload_size, size_t max_le_payload_size) {
TestingBase::SetUp();
acl_data_channel_.set_bredr_buffer_info(
hci::DataBufferInfo(max_acl_payload_size, /*max_num_packets=*/1));
acl_data_channel_.set_le_buffer_info(
hci::DataBufferInfo(max_le_payload_size, /*max_num_packets=*/1));
acl_data_channel_.set_send_packets_cb(
fit::bind_member<&ChannelManagerMockAclChannelTest::SendPackets>(this));
// TODO(https://fxbug.dev/42141538): Make these tests not depend on strict
// channel ID ordering.
chanmgr_ = ChannelManager::Create(&acl_data_channel_,
transport()->command_channel(),
/*random_channel_ids=*/false,
dispatcher());
packet_rx_handler_ = [this](std::unique_ptr<hci::ACLDataPacket> packet) {
acl_data_channel_.ReceivePacket(std::move(packet));
};
next_command_id_ = 1;
}
void TearDown() override {
while (!expected_packets_.empty()) {
auto& expected = expected_packets_.front();
ADD_FAILURE_AT(expected.file_name, expected.line_number)
<< "Didn't receive expected outbound " << expected.data.size()
<< "-byte packet";
expected_packets_.pop();
}
chanmgr_ = nullptr;
packet_rx_handler_ = nullptr;
TestingBase::TearDown();
}
// Helper functions for registering logical links with default arguments.
[[nodiscard]] ChannelManager::LEFixedChannels RegisterLE(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb = DoNothing,
LEConnectionParameterUpdateCallback cpuc = NopLeConnParamCallback,
SecurityUpgradeCallback suc = NopSecurityCallback) {
return chanmgr()->AddLEConnection(handle,
role,
std::move(link_error_cb),
std::move(cpuc),
std::move(suc));
}
struct QueueRegisterACLRetVal {
CommandId extended_features_id;
CommandId fixed_channels_supported_id;
ChannelManager::BrEdrFixedChannels fixed_channels;
};
QueueRegisterACLRetVal QueueRegisterACL(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb = DoNothing,
SecurityUpgradeCallback suc = NopSecurityCallback) {
QueueRegisterACLRetVal return_val;
return_val.extended_features_id = NextCommandId();
return_val.fixed_channels_supported_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(MakeExtendedFeaturesInformationRequest(
return_val.extended_features_id, handle),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(testing::AclFixedChannelsSupportedInfoReq(
return_val.fixed_channels_supported_id, handle),
kHighPriority);
return_val.fixed_channels =
RegisterACL(handle, role, std::move(link_error_cb), std::move(suc));
return return_val;
}
ChannelManager::BrEdrFixedChannels RegisterACL(
hci_spec::ConnectionHandle handle,
pw::bluetooth::emboss::ConnectionRole role,
LinkErrorCallback link_error_cb = DoNothing,
SecurityUpgradeCallback suc = NopSecurityCallback) {
return chanmgr()->AddACLConnection(
handle, role, std::move(link_error_cb), std::move(suc));
}
void ReceiveL2capInformationResponses(
CommandId extended_features_id,
CommandId fixed_channels_supported_id,
l2cap::ExtendedFeatures features = 0u,
l2cap::FixedChannelsSupported channels = 0u) {
ReceiveAclDataPacket(testing::AclExtFeaturesInfoRsp(
extended_features_id, kTestHandle1, features));
ReceiveAclDataPacket(testing::AclFixedChannelsSupportedInfoRsp(
fixed_channels_supported_id, kTestHandle1, channels));
}
Channel::WeakPtr ActivateNewFixedChannel(
ChannelId id,
hci_spec::ConnectionHandle conn_handle = kTestHandle1,
Channel::ClosedCallback closed_cb = DoNothing,
Channel::RxCallback rx_cb = NopRxCallback) {
auto chan = chanmgr()->OpenFixedChannel(conn_handle, id);
if (!chan.is_alive() ||
!chan->Activate(std::move(rx_cb), std::move(closed_cb))) {
return Channel::WeakPtr();
}
return chan;
}
// |activated_cb| will be called with opened and activated Channel if
// successful and nullptr otherwise.
void ActivateOutboundChannel(
Psm psm,
ChannelParameters chan_params,
ChannelCallback activated_cb,
hci_spec::ConnectionHandle conn_handle = kTestHandle1,
Channel::ClosedCallback closed_cb = DoNothing,
Channel::RxCallback rx_cb = NopRxCallback) {
ChannelCallback open_cb = [activated_cb = std::move(activated_cb),
rx_cb = std::move(rx_cb),
closed_cb =
std::move(closed_cb)](auto chan) mutable {
if (!chan.is_alive() ||
!chan->Activate(std::move(rx_cb), std::move(closed_cb))) {
activated_cb(Channel::WeakPtr());
} else {
activated_cb(std::move(chan));
}
};
chanmgr()->OpenL2capChannel(
conn_handle, psm, chan_params, std::move(open_cb));
}
void SetUpOutboundChannelWithCallback(
ChannelId local_id,
ChannelId remote_id,
Channel::ClosedCallback closed_cb,
ChannelParameters channel_params,
fit::function<void(Channel::WeakPtr)> channel_cb) {
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(testing::AclConnectionReq(
conn_req_id, kTestHandle1, local_id, kTestPsm),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
testing::AclConfigReq(
config_req_id, kTestHandle1, remote_id, kChannelParams),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
testing::AclConfigRsp(
kPeerConfigRequestId, kTestHandle1, remote_id, kChannelParams),
kHighPriority);
ActivateOutboundChannel(kTestPsm,
channel_params,
std::move(channel_cb),
kTestHandle1,
std::move(closed_cb));
RunUntilIdle();
ReceiveAclDataPacket(testing::AclConnectionRsp(
conn_req_id, kTestHandle1, local_id, remote_id));
ReceiveAclDataPacket(testing::AclConfigReq(
kPeerConfigRequestId, kTestHandle1, local_id, kChannelParams));
ReceiveAclDataPacket(testing::AclConfigRsp(
config_req_id, kTestHandle1, local_id, kChannelParams));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
}
Channel::WeakPtr SetUpOutboundChannel(
ChannelId local_id = kLocalId,
ChannelId remote_id = kRemoteId,
Channel::ClosedCallback closed_cb = DoNothing,
ChannelParameters channel_params = kChannelParams) {
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr activated_chan) {
channel = std::move(activated_chan);
};
SetUpOutboundChannelWithCallback(
local_id, remote_id, std::move(closed_cb), channel_params, channel_cb);
EXPECT_TRUE(channel.is_alive());
return channel;
}
// Set an expectation for an outbound ACL data packet. Packets are expected in
// the order that they're added. The test fails if not all expected packets
// have been set when the test case completes or if the outbound data doesn't
// match expectations, including the ordering between LE and ACL packets.
void ExpectOutboundPacket(bt::LinkType ll_type,
hci::AclDataChannel::PacketPriority priority,
const ByteBuffer& data,
const char* file_name = "",
int line_number = 0) {
expected_packets_.push(
{file_name, line_number, DynamicByteBuffer(data), ll_type, priority});
}
void ActivateOutboundErtmChannel(
ChannelCallback activated_cb,
hci_spec::ConnectionHandle conn_handle = kTestHandle1,
uint8_t max_outbound_transmit = 3,
Channel::ClosedCallback closed_cb = DoNothing,
Channel::RxCallback rx_cb = NopRxCallback) {
l2cap::ChannelParameters chan_params;
auto config_mode =
l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission;
chan_params.mode = config_mode;
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
OutboundConfigurationRequest(
config_req_id, kMaxInboundPduPayloadSize, config_mode),
kHighPriority);
const auto kInboundMtu = kDefaultMTU;
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId,
kInboundMtu,
config_mode,
max_outbound_transmit),
kHighPriority);
ActivateOutboundChannel(kTestPsm,
chan_params,
std::move(activated_cb),
conn_handle,
std::move(closed_cb),
std::move(rx_cb));
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(
kPeerConfigRequestId, kInboundMtu, config_mode, max_outbound_transmit));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
}
// Returns true if all expected outbound packets up to this call have been
// sent by the test case.
[[nodiscard]] bool AllExpectedPacketsSent() const {
return expected_packets_.empty();
}
void ReceiveAclDataPacket(const ByteBuffer& packet) {
const size_t payload_size = packet.size() - sizeof(hci_spec::ACLDataHeader);
BT_ASSERT(payload_size <= std::numeric_limits<uint16_t>::max());
hci::ACLDataPacketPtr acl_packet =
hci::ACLDataPacket::New(static_cast<uint16_t>(payload_size));
auto mutable_acl_packet_data = acl_packet->mutable_view()->mutable_data();
packet.Copy(&mutable_acl_packet_data);
packet_rx_handler_(std::move(acl_packet));
}
ChannelManager* chanmgr() const { return chanmgr_.get(); }
hci::testing::MockAclDataChannel* acl_data_channel() {
return &acl_data_channel_;
}
CommandId NextCommandId() { return next_command_id_++; }
private:
bool SendPackets(std::list<hci::ACLDataPacketPtr> packets) {
for (auto& packet : packets) {
const ByteBuffer& data = packet->view().data();
if (expected_packets_.empty()) {
ADD_FAILURE() << "Unexpected outbound ACL data";
std::cout << "{ ";
PrintByteContainer(data);
std::cout << " }\n";
} else {
const auto& expected = expected_packets_.front();
// Prints both data in case of mismatch.
if (!ContainersEqual(expected.data, data)) {
ADD_FAILURE_AT(expected.file_name, expected.line_number)
<< "Outbound ACL data doesn't match expected";
}
expected_packets_.pop();
}
}
return !packets.empty();
}
std::unique_ptr<ChannelManager> chanmgr_;
hci::ACLPacketHandler packet_rx_handler_;
hci::testing::MockAclDataChannel acl_data_channel_;
std::queue<const PacketExpectation> expected_packets_;
CommandId next_command_id_;
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ChannelManagerMockAclChannelTest);
};
// ChannelManager test fixture that uses a real AclDataChannel and uses
// MockController for HCI packet expectations
using ChannelManagerRealAclChannelTest =
FakeDispatcherChannelManagerMockControllerTest;
TEST_F(ChannelManagerRealAclChannelTest, OpenFixedChannelErrorNoConn) {
// This should fail as the ChannelManager has no entry for |kTestHandle1|.
EXPECT_FALSE(ActivateNewFixedChannel(kATTChannelId).is_alive());
QueueLEConnection(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// This should fail as the ChannelManager has no entry for |kTestHandle2|.
EXPECT_FALSE(ActivateNewFixedChannel(kATTChannelId, kTestHandle2).is_alive());
}
TEST_F(ChannelManagerRealAclChannelTest, OpenFixedChannelErrorDisallowedId) {
// LE-U link
QueueLEConnection(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// ACL-U link
QueueAclConnection(kTestHandle2);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
// This should fail as kSMPChannelId is ACL-U only.
EXPECT_FALSE(ActivateNewFixedChannel(kSMPChannelId, kTestHandle1).is_alive());
// This should fail as kATTChannelId is LE-U only.
EXPECT_FALSE(ActivateNewFixedChannel(kATTChannelId, kTestHandle2).is_alive());
}
TEST_F(ChannelManagerRealAclChannelTest,
DeactivateDynamicChannelInvalidatesChannelPointer) {
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kTestPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel.is_alive());
ASSERT_TRUE(channel->Activate(NopRxCallback, DoNothing));
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclDisconnectionReq(
NextCommandId(), kTestHandle1, kLocalId, kRemoteId));
channel->Deactivate();
RunUntilIdle();
ASSERT_FALSE(channel.is_alive());
}
TEST_F(ChannelManagerRealAclChannelTest,
DeactivateAttChannelInvalidatesChannelPointer) {
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
fixed_channels.att->Deactivate();
ASSERT_FALSE(fixed_channels.att.is_alive());
}
TEST_F(ChannelManagerRealAclChannelTest, OpenFixedChannelAndUnregisterLink) {
// LE-U link
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, closed_cb));
EXPECT_EQ(kTestHandle1, fixed_channels.att->link_handle());
// This should notify the channel.
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
// |closed_cb| will be called synchronously since it was registered using the
// current thread's task runner.
EXPECT_TRUE(closed_called);
}
TEST_F(ChannelManagerRealAclChannelTest, OpenFixedChannelAndCloseChannel) {
// LE-U link
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, closed_cb));
// Close the channel before unregistering the link. |closed_cb| should not get
// called.
fixed_channels.att->Deactivate();
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
EXPECT_FALSE(closed_called);
}
TEST_F(ChannelManagerRealAclChannelTest, FixedChannelsUseBasicMode) {
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
EXPECT_EQ(RetransmissionAndFlowControlMode::kBasic,
fixed_channels.att->mode());
}
TEST_F(ChannelManagerRealAclChannelTest,
OpenAndCloseWithLinkMultipleFixedChannels) {
// LE-U link
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool att_closed = false;
auto att_closed_cb = [&att_closed] { att_closed = true; };
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, att_closed_cb));
bool smp_closed = false;
auto smp_closed_cb = [&smp_closed] { smp_closed = true; };
ASSERT_TRUE(fixed_channels.smp->Activate(NopRxCallback, smp_closed_cb));
fixed_channels.smp->Deactivate();
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(att_closed);
EXPECT_FALSE(smp_closed);
}
TEST_F(ChannelManagerRealAclChannelTest,
SendingPacketsBeforeRemoveConnectionAndVerifyChannelClosed) {
// LE-U link
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = fixed_channels.att;
ASSERT_TRUE(chan.is_alive());
ASSERT_TRUE(chan->Activate(NopRxCallback, closed_cb));
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame (length: 2, channel-id: ATT)
0x02,
0x00,
LowerBits(kATTChannelId),
UpperBits(kATTChannelId),
'h',
'i'));
EXPECT_TRUE(chan->Send(NewBuffer('h', 'i')));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
chanmgr()->RemoveConnection(kTestHandle1);
// The L2CAP channel should have been notified of closure immediately.
EXPECT_TRUE(closed_called);
EXPECT_FALSE(chan.is_alive());
RunUntilIdle();
}
// Tests that destroying the ChannelManager cleanly shuts down all channels.
TEST_F(ChannelManagerRealAclChannelTest,
DestroyingChannelManagerCleansUpChannels) {
// LE-U link
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
auto chan = fixed_channels.att;
ASSERT_TRUE(chan.is_alive());
ASSERT_TRUE(chan->Activate(NopRxCallback, closed_cb));
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame (length: 2, channel-id: ATT)
0x02,
0x00,
LowerBits(kATTChannelId),
UpperBits(kATTChannelId),
'h',
'i'));
// Send a packet. This should be processed immediately.
EXPECT_TRUE(chan->Send(NewBuffer('h', 'i')));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
TearDown();
EXPECT_TRUE(closed_called);
EXPECT_FALSE(chan.is_alive());
// No outbound packet expectations were set, so this test will fail if it
// sends any data.
RunUntilIdle();
}
TEST_F(ChannelManagerRealAclChannelTest, 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).
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att.is_alive());
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
fixed_channels.att->Deactivate();
// Loop until the clean up task runs.
RunUntilIdle();
}
TEST_F(ChannelManagerRealAclChannelTest,
CallingDeactivateFromClosedCallbackDoesNotCrashOrHang) {
BrEdrFixedChannels fixed_channels =
QueueAclConnection(kTestHandle1).fixed_channels;
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
auto chan = std::move(fixed_channels.smp);
chan->Activate(NopRxCallback, [chan] { chan->Deactivate(); });
chanmgr()->RemoveConnection(kTestHandle1); // Triggers ClosedCallback.
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest, ReceiveData) {
// LE-U link
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att.is_alive());
ASSERT_TRUE(fixed_channels.smp.is_alive());
// 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) {
BT_DEBUG_ASSERT(sdu);
sdus.push_back(sdu->ToString());
};
bool smp_cb_called = false;
auto smp_rx_cb = [&smp_cb_called](ByteBufferPtr sdu) {
BT_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
ASSERT_TRUE(fixed_channels.att->Activate(att_rx_cb, DoNothing));
ASSERT_TRUE(fixed_channels.smp->Activate(smp_rx_cb, DoNothing));
// ATT channel
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x09,
0x00,
// L2CAP B-frame
0x05,
0x00,
0x04,
0x00,
'h',
'e',
'l',
'l',
'o'));
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x09,
0x00,
// L2CAP B-frame (partial)
0x0C,
0x00,
0x04,
0x00,
'h',
'o',
'w',
' ',
'a'));
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (continuing fragment)
0x01,
0x10,
0x07,
0x00,
// L2CAP B-frame (partial)
'r',
'e',
' ',
'y',
'o',
'u',
'?'));
// SMP channel
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame (empty)
0x00,
0x00,
0x06,
0x00));
RunUntilIdle();
EXPECT_TRUE(smp_cb_called);
ASSERT_EQ(2u, sdus.size());
EXPECT_EQ("hello", sdus[0]);
EXPECT_EQ("how are you?", sdus[1]);
}
TEST_F(ChannelManagerMockAclChannelTest, 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) {
BT_DEBUG_ASSERT(sdu);
EXPECT_EQ(0u, sdu->size());
smp_cb_called = true;
};
// ATT channel
for (size_t i = 0u; i < kPacketCount; i++) {
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame
0x00,
0x00,
0x04,
0x00));
}
// SMP channel
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame (empty)
0x00,
0x00,
0x06,
0x00));
Channel::WeakPtr att_chan, smp_chan;
// Run the loop so all packets are received.
RunUntilIdle();
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(att_rx_cb, DoNothing));
ASSERT_TRUE(fixed_channels.smp->Activate(smp_rx_cb, DoNothing));
RunUntilIdle();
EXPECT_TRUE(smp_cb_called);
EXPECT_EQ(kPacketCount, packet_count);
}
// Receive data after registering the link but before creating a fixed channel.
TEST_F(ChannelManagerRealAclChannelTest,
ReceiveDataBeforeCreatingFixedChannel) {
constexpr size_t kPacketCount = 10;
// Register an ACL connection because LE connections create fixed channels
// immediately.
QueueAclConnection(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
StaticByteBuffer<255> buffer;
size_t packet_count = 0;
auto rx_cb = [&packet_count](ByteBufferPtr sdu) { packet_count++; };
for (size_t i = 0u; i < kPacketCount; i++) {
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (starting fragment)
LowerBits(kTestHandle1),
UpperBits(kTestHandle1),
0x04,
0x00,
// L2CAP B-frame (empty)
0x00,
0x00,
LowerBits(kConnectionlessChannelId),
UpperBits(kConnectionlessChannelId)));
}
// Run the loop so all packets are received.
RunUntilIdle();
auto chan = ActivateNewFixedChannel(
kConnectionlessChannelId, kTestHandle1, DoNothing, std::move(rx_cb));
RunUntilIdle();
EXPECT_EQ(kPacketCount, packet_count);
}
// Receive data after registering the link and creating the channel but before
// setting the rx handler.
TEST_F(ChannelManagerRealAclChannelTest, ReceiveDataBeforeSettingRxHandler) {
constexpr size_t kPacketCount = 10;
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
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) {
BT_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(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame
0x00,
0x00,
LowerBits(kATTChannelId),
UpperBits(kATTChannelId)));
}
// SMP channel
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame (empty)
0x00,
0x00,
LowerBits(kLESMPChannelId),
UpperBits(kLESMPChannelId)));
// Run the loop so all packets are received.
RunUntilIdle();
fixed_channels.att->Activate(att_rx_cb, DoNothing);
fixed_channels.smp->Activate(smp_rx_cb, DoNothing);
RunUntilIdle();
EXPECT_TRUE(smp_cb_called);
EXPECT_EQ(kPacketCount, packet_count);
}
TEST_F(ChannelManagerMockAclChannelTest,
ActivateChannelProcessesCallbacksSynchronously) {
// LE-U link
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
int att_rx_cb_count = 0;
int smp_rx_cb_count = 0;
auto att_rx_cb = [&att_rx_cb_count](ByteBufferPtr sdu) {
EXPECT_EQ("hello", sdu->AsString());
att_rx_cb_count++;
};
bool att_closed_called = false;
auto att_closed_cb = [&att_closed_called] { att_closed_called = true; };
ASSERT_TRUE(fixed_channels.att->Activate(std::move(att_rx_cb),
std::move(att_closed_cb)));
auto smp_rx_cb = [&smp_rx_cb_count](ByteBufferPtr sdu) {
EXPECT_EQ("🤨", sdu->AsString());
smp_rx_cb_count++;
};
bool smp_closed_called = false;
auto smp_closed_cb = [&smp_closed_called] { smp_closed_called = true; };
ASSERT_TRUE(fixed_channels.smp->Activate(std::move(smp_rx_cb),
std::move(smp_closed_cb)));
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x08,
0x00,
// L2CAP B-frame for SMP fixed channel (4-byte payload: U+1F928 in UTF-8)
0x04,
0x00,
0x06,
0x00,
0xf0,
0x9f,
0xa4,
0xa8));
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00,
0x09,
0x00,
// L2CAP B-frame for ATT fixed channel
0x05,
0x00,
0x04,
0x00,
'h',
'e',
'l',
'l',
'o'));
// Receiving data in ChannelManager processes the ATT and SMP packets
// synchronously so it has already routed the data to the Channels.
EXPECT_EQ(1, att_rx_cb_count);
EXPECT_EQ(1, smp_rx_cb_count);
RunUntilIdle();
EXPECT_EQ(1, att_rx_cb_count);
EXPECT_EQ(1, smp_rx_cb_count);
// Link closure synchronously calls the ATT and SMP channel close callbacks.
chanmgr()->RemoveConnection(kTestHandle1);
EXPECT_TRUE(att_closed_called);
EXPECT_TRUE(smp_closed_called);
}
TEST_F(ChannelManagerRealAclChannelTest,
RemovingLinkInvalidatesChannelPointer) {
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
BT_ASSERT(fixed_channels.att->Activate(NopRxCallback, DoNothing));
chanmgr()->RemoveConnection(kTestHandle1);
EXPECT_FALSE(fixed_channels.att.is_alive());
}
TEST_F(ChannelManagerRealAclChannelTest, SendBasicSDU) {
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
BT_ASSERT(fixed_channels.att->Activate(NopRxCallback, DoNothing));
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// 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(fixed_channels.att->Send(NewBuffer('T', 'e', 's', 't')));
RunUntilIdle();
}
// Tests that fragmentation of BR/EDR packets uses the BR/EDR buffer size
TEST_F(ChannelManagerRealAclChannelTest, SendBREDRFragmentedSDUs) {
// Customize setup so that ACL payload size is 6 instead of default 1024
TearDown();
SetUp(/*max_acl_payload_size=*/6, /*max_le_payload_size=*/0);
// Send fragmented Extended Features Information Request
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame (length: 6, channel-id: 1)
0x06,
0x00,
0x01,
0x00,
// Extended Features Information Request
// (code = 0x0A, ID)
0x0A,
NextCommandId()));
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, pbf: continuing fr., length: 4)
0x01,
0x10,
0x04,
0x00,
// Extended Features Information Request cont. (Length: 2, type)
0x02,
0x00,
LowerBits(static_cast<uint16_t>(
InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(
InformationType::kExtendedFeaturesSupported))));
// Send fragmented Fixed Channels Supported Information Request
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame (length: 6, channel-id: 1)
0x06,
0x00,
0x01,
0x00,
// Fixed Channels Supported Information Request
// (command code, command ID)
l2cap::kInformationRequest,
NextCommandId()));
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, pbf: continuing fr., length: 4)
0x01,
0x10,
0x04,
0x00,
// Fixed Channels Supported Information Request cont.
// (length: 2, type)
0x02,
0x00,
LowerBits(
static_cast<uint16_t>(InformationType::kFixedChannelsSupported)),
UpperBits(static_cast<uint16_t>(
InformationType::kFixedChannelsSupported))));
BrEdrFixedChannels fixed_channels = chanmgr()->AddACLConnection(
kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
/*link_error_callback=*/[]() {},
/*security_callback=*/[](auto, auto, auto) {});
Channel::WeakPtr sm_chan = std::move(fixed_channels.smp);
sm_chan->Activate(NopRxCallback, DoNothing);
ASSERT_TRUE(sm_chan.is_alive());
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame: (length: 7, channel-id: 7 (SMP), partial payload)
0x07,
0x00,
LowerBits(kSMPChannelId),
UpperBits(kSMPChannelId),
'G',
'o'));
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, pbf: continuing fr., length: 5)
0x01,
0x10,
0x05,
0x00,
// continuing payload
'o',
'd',
'b',
'y',
'e'));
// SDU of length 7 corresponds to a 11-octet B-frame
// Due to the BR/EDR buffer size, this should be sent over a 6-byte then a
// 5-byte fragment.
EXPECT_TRUE(sm_chan->Send(NewBuffer('G', 'o', 'o', 'd', 'b', 'y', 'e')));
RunUntilIdle();
}
TEST_F(ChannelManagerRealAclChannelTest,
SendBREDRFragmentedSDUsOverTwoDynamicChannels) {
// Customize setup so that ACL payload size is 18 instead of default 1024
TearDown();
SetUp(/*max_acl_payload_size=*/18, /*max_le_payload_size=*/0);
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/response, config request, config response
constexpr size_t kChannelCreationPacketCount = 3;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
l2cap::Channel::WeakPtr channel0;
auto chan_cb0 = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel0 = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm0, kLocalId0, kRemoteId0, std::move(chan_cb0));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(channel0.is_alive());
ASSERT_TRUE(channel0->Activate(NopRxCallback, DoNothing));
l2cap::Channel::WeakPtr channel1;
auto chan_cb1 = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel1 = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm1, kLocalId1, kRemoteId1, std::move(chan_cb1));
// Free up the buffer space from packets sent while creating |channel0|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1, kChannelCreationPacketCount));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel1.is_alive());
ASSERT_TRUE(channel1->Activate(NopRxCallback, DoNothing));
// Free up the buffer space from packets sent while creating |channel1|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1,
kConnectionCreationPacketCount + kChannelCreationPacketCount));
RunUntilIdle();
// Queue size should be equal to or larger than |num_queued_packets| to ensure
// that all packets get queued and sent
uint16_t num_queued_packets = 3;
EXPECT_TRUE(num_queued_packets <= kDefaultTxMaxQueuedCount);
// Queue 14 packets in total, distributed between the two channels
// Fill up BR/EDR controller buffer then queue 3 additional packets (which
// will be later fragmented to form 6 packets)
for (size_t i = 0; i < kBufferMaxNumPackets / 2 + num_queued_packets; i++) {
Channel::WeakPtr channel = (i % 2) ? channel1 : channel0;
ChannelId channel_id = (i % 2) ? kRemoteId1 : kRemoteId0;
const StaticByteBuffer kPacket0(
// ACL data header (handle: 1, length: 18)
0x01,
0x00,
0x12,
0x00,
// L2CAP B-frame: (length: 14, channel-id, partial payload)
0x14,
0x00,
LowerBits(channel_id),
UpperBits(channel_id),
'G',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o');
EXPECT_ACL_PACKET_OUT(test_device(), kPacket0);
const StaticByteBuffer kPacket1(
// ACL data header (handle: 1, pbf: continuing fr., length: 6)
0x01,
0x10,
0x06,
0x00,
// continuing payload
'o',
'o',
'd',
'b',
'y',
'e');
EXPECT_ACL_PACKET_OUT(test_device(), kPacket1);
// SDU of length 20 corresponds to a 24-octet B-frame
// Due to the BR/EDR buffer size, this should be sent over a 18-byte then a
// 6-byte fragment.
EXPECT_TRUE(channel->Send(NewBuffer('G',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'o',
'd',
'b',
'y',
'e')));
RunUntilIdle();
}
EXPECT_FALSE(test_device()->AllExpectedDataPacketsSent());
// Notify the processed packets with a Number Of Completed Packet HCI event
// This should cause the remaining 6 fragmented packets to be sent
test_device()->SendCommandChannelPacket(
NumberOfCompletedPacketsPacket(kTestHandle1, 6));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
}
// Tests that fragmentation of LE packets uses the LE buffer size
TEST_F(ChannelManagerRealAclChannelTest, SendLEFragmentedSDUs) {
TearDown();
SetUp(/*max_acl_payload_size=*/6, /*max_le_payload_size=*/5);
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// 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'));
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 1, pbf: continuing fr., length: 4)
0x01,
0x10,
0x04,
0x00,
// Continuing payload
'e',
'l',
'l',
'o'));
// SDU of length 5 corresponds to a 9-octet B-frame which should be sent over
// a 5-byte and a 4-byte fragment.
EXPECT_TRUE(fixed_channels.att->Send(NewBuffer('H', 'e', 'l', 'l', 'o')));
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest, ACLChannelSignalLinkError) {
bool link_error = false;
auto link_error_cb = [&link_error] { link_error = true; };
QueueRegisterACLRetVal acl =
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
link_error_cb);
acl.fixed_channels.smp->Activate(NopRxCallback, DoNothing);
acl.fixed_channels.smp->SignalLinkError();
RunUntilIdle();
EXPECT_TRUE(link_error);
}
TEST_F(ChannelManagerMockAclChannelTest, LEChannelSignalLinkError) {
bool link_error = false;
auto link_error_cb = [&link_error] { link_error = true; };
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
link_error_cb);
// Activate a new Attribute channel to signal the error.
fixed_channels.att->Activate(NopRxCallback, DoNothing);
fixed_channels.att->SignalLinkError();
RunUntilIdle();
EXPECT_TRUE(link_error);
}
TEST_F(ChannelManagerMockAclChannelTest, SignalLinkErrorDisconnectsChannels) {
bool link_error = false;
auto link_error_cb = [this, &link_error] {
// This callback is run after the expectation for
// OutboundDisconnectionRequest is set, so this tests that L2CAP-level
// teardown happens before ChannelManager requests a link teardown.
ASSERT_TRUE(AllExpectedPacketsSent());
link_error = true;
// Simulate closing the link.
chanmgr()->RemoveConnection(kTestHandle1);
};
QueueRegisterACLRetVal acl =
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
link_error_cb);
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
Channel::WeakPtr dynamic_channel;
auto channel_cb = [&dynamic_channel](l2cap::Channel::WeakPtr activated_chan) {
dynamic_channel = std::move(activated_chan);
};
int dynamic_channel_closed = 0;
ActivateOutboundChannel(
kTestPsm,
kChannelParams,
std::move(channel_cb),
kTestHandle1,
/*closed_cb=*/[&dynamic_channel_closed] { dynamic_channel_closed++; });
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RETURN_IF_FATAL(RunUntilIdle());
EXPECT_TRUE(AllExpectedPacketsSent());
// The channel on kTestHandle1 should be open.
EXPECT_TRUE(dynamic_channel.is_alive());
EXPECT_EQ(0, dynamic_channel_closed);
EXPECT_TRUE(AllExpectedPacketsSent());
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
// Activate a new Security Manager channel to signal the error on
// kTestHandle1.
int fixed_channel_closed = 0;
acl.fixed_channels.smp->Activate(
NopRxCallback,
/*closed_callback=*/[&fixed_channel_closed] { fixed_channel_closed++; });
ASSERT_FALSE(link_error);
acl.fixed_channels.smp->SignalLinkError();
RETURN_IF_FATAL(RunUntilIdle());
// link_error_cb is not called until Disconnection Response is received for
// each dynamic channel
EXPECT_FALSE(link_error);
// But channels should be deactivated to prevent any activity.
EXPECT_EQ(1, fixed_channel_closed);
EXPECT_EQ(1, dynamic_channel_closed);
ASSERT_TRUE(AllExpectedPacketsSent());
const auto disconnection_rsp = testing::AclDisconnectionRsp(
disconn_req_id, kTestHandle1, kLocalId, kRemoteId);
ReceiveAclDataPacket(disconnection_rsp);
RETURN_IF_FATAL(RunUntilIdle());
EXPECT_TRUE(link_error);
}
TEST_F(ChannelManagerRealAclChannelTest, LEConnectionParameterUpdateRequest) {
EXPECT_ACL_PACKET_OUT(
test_device(),
StaticByteBuffer(
// ACL data header (handle: 0x0001, length: 10 bytes)
0x01,
0x00,
0x0a,
0x00,
// L2CAP B-frame header (length: 6 bytes, channel-id: 0x0005 (LEsig))
0x06,
0x00,
0x05,
0x00,
// L2CAP C-frame header
// (LE conn. param. update response, id: 1, length: 2 bytes)
0x13,
0x01,
0x02,
0x00,
// res: accepted
0x00,
0x00));
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att.is_alive());
ASSERT_TRUE(fixed_channels.smp.is_alive());
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
ASSERT_TRUE(fixed_channels.smp->Activate(NopRxCallback, DoNothing));
// clang-format off
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// 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 expectations in |conn_param_cb|).
0x06, 0x00,
0x80, 0x0C,
0xF3, 0x01,
0x80, 0x0C));
// clang-format on
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
ACLOutboundDynamicChannelLocalDisconnect) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr activated_chan) {
channel = std::move(activated_chan);
};
bool closed_cb_called = false;
auto closed_cb = [&closed_cb_called] { closed_cb_called = true; };
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
ActivateOutboundChannel(kTestPsm,
kChannelParams,
std::move(channel_cb),
kTestHandle1,
std::move(closed_cb));
RunUntilIdle();
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
ASSERT_TRUE(channel.is_alive());
EXPECT_FALSE(closed_cb_called);
EXPECT_EQ(kLocalId, channel->id());
EXPECT_EQ(kRemoteId, channel->remote_id());
EXPECT_EQ(RetransmissionAndFlowControlMode::kBasic, channel->mode());
// Test SDU transmission.
// SDU must have remote channel ID (unlike for fixed channels).
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01,
0x00,
0x08,
0x00,
// L2CAP B-frame: (length: 4, channel-id)
0x04,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
'T',
'e',
's',
't'),
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('T', 'e', 's', 't')));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
// Explicit deactivation should not res in |closed_cb| being called.
channel->Deactivate();
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
// Ensure callback is not called after the channel has disconnected
acl_data_channel()->set_drop_queued_packets_cb(nullptr);
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// 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, length: 4, dst cid, src cid)
0x07, disconn_req_id, 0x04, 0x00,
LowerBits(kRemoteId), UpperBits(kRemoteId), LowerBits(kLocalId), UpperBits(kLocalId)));
// clang-format on
RunUntilIdle();
EXPECT_FALSE(closed_cb_called);
}
TEST_F(ChannelManagerRealAclChannelTest,
ACLOutboundDynamicChannelRemoteDisconnect) {
QueueAclConnection(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kTestPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel.is_alive());
bool dynamic_channel_closed = false;
ASSERT_TRUE(channel->Activate(NopRxCallback,
/*closed_callback=*/[&dynamic_channel_closed] {
dynamic_channel_closed = true;
}));
EXPECT_FALSE(dynamic_channel_closed);
EXPECT_EQ(kLocalId, channel->id());
EXPECT_EQ(kRemoteId, channel->remote_id());
EXPECT_EQ(RetransmissionAndFlowControlMode::kBasic, channel->mode());
// Test SDU reception.
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01,
0x00,
0x08,
0x00,
// L2CAP B-frame: (length: 4, channel-id)
0x04,
0x00,
LowerBits(kLocalId),
UpperBits(kLocalId),
'T',
'e',
's',
't'));
RunUntilIdle();
EXPECT_ACL_PACKET_OUT(test_device(), OutboundDisconnectionResponse(7));
// clang-format off
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// 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, src cid)
0x06, 0x07, 0x04, 0x00,
LowerBits(kLocalId), UpperBits(kLocalId), LowerBits(kRemoteId), UpperBits(kRemoteId)));
// clang-format on
// The preceding peer disconnection should have immediately destroyed the
// route to the channel. L2CAP will process it and this following SDU
// back-to-back. The latter should be dropped.
test_device()->SendACLDataChannelPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 5)
0x01,
0x00,
0x05,
0x00,
// L2CAP B-frame: (length: 1, channel-id: 0x0040)
0x01,
0x00,
0x40,
0x00,
'!'));
RunUntilIdle();
EXPECT_TRUE(dynamic_channel_closed);
}
TEST_F(ChannelManagerMockAclChannelTest,
ACLOutboundDynamicChannelDataNotBuffered) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr 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.
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01,
0x00,
0x08,
0x00,
// L2CAP B-frame: (length: 4, channel-id)
0x04,
0x00,
LowerBits(kLocalId),
UpperBits(kLocalId),
'T',
'e',
's',
't'));
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
ActivateOutboundChannel(kTestPsm,
kChannelParams,
std::move(channel_cb),
kTestHandle1,
std::move(closed_cb),
std::move(data_rx_cb));
RunUntilIdle();
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
// The channel is connected but not configured, so no data should flow on the
// channel. Test that this received data is also ignored.
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id)
0x04, 0x00, LowerBits(kLocalId), UpperBits(kLocalId), 'T', 'e', 's', 't'));
// clang-format on
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_FALSE(channel_closed);
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionResponse(7), kHighPriority);
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// 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, src cid)
0x06, 0x07, 0x04, 0x00,
LowerBits(kLocalId), UpperBits(kLocalId), LowerBits(kRemoteId), UpperBits(kRemoteId)));
// clang-format on
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
ACLOutboundDynamicChannelRemoteRefused) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool channel_cb_called = false;
auto channel_cb = [&channel_cb_called](l2cap::Channel::WeakPtr channel) {
channel_cb_called = true;
EXPECT_FALSE(channel.is_alive());
};
const CommandId conn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
ActivateOutboundChannel(kTestPsm, kChannelParams, std::move(channel_cb));
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// 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, length: 8, dst cid: 0x0000 (invalid),
// src cid, res: 0x0004 (Refused; no resources available), status: none)
0x03, conn_req_id, 0x08, 0x00,
0x00, 0x00, LowerBits(kLocalId), UpperBits(kLocalId),
0x04, 0x00, 0x00, 0x00));
// clang-format on
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel_cb_called);
}
TEST_F(ChannelManagerMockAclChannelTest,
ACLOutboundDynamicChannelFailedConfiguration) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool channel_cb_called = false;
auto channel_cb = [&channel_cb_called](l2cap::Channel::WeakPtr channel) {
channel_cb_called = true;
EXPECT_FALSE(channel.is_alive());
};
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
ActivateOutboundChannel(kTestPsm, kChannelParams, std::move(channel_cb));
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// 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, length: 6, src cid, flags: 0,
// res: 0x0002 (Rejected; no reason provided))
0x05, config_req_id, 0x06, 0x00,
LowerBits(kLocalId), UpperBits(kLocalId), 0x00, 0x00,
0x02, 0x00));
ReceiveAclDataPacket(StaticByteBuffer(
// 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, length: 4, dst cid, src cid)
0x07, disconn_req_id, 0x04, 0x00,
LowerBits(kRemoteId), UpperBits(kRemoteId), LowerBits(kLocalId), UpperBits(kLocalId)));
// clang-format on
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel_cb_called);
}
TEST_F(ChannelManagerMockAclChannelTest,
ACLInboundDynamicChannelLocalDisconnect) {
constexpr Psm kBadPsm0 = 0x0004;
constexpr Psm kBadPsm1 = 0x0103;
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
bool dynamic_channel_closed = false;
auto closed_cb = [&dynamic_channel_closed] { dynamic_channel_closed = true; };
Channel::WeakPtr channel;
auto channel_cb = [&channel, closed_cb = std::move(closed_cb)](
l2cap::Channel::WeakPtr opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, std::move(closed_cb)));
};
EXPECT_FALSE(
chanmgr()->RegisterService(kBadPsm0, ChannelParameters(), channel_cb));
EXPECT_FALSE(
chanmgr()->RegisterService(kBadPsm1, ChannelParameters(), channel_cb));
EXPECT_TRUE(chanmgr()->RegisterService(
kTestPsm, ChannelParameters(), std::move(channel_cb)));
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionResponse(1), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
ReceiveAclDataPacket(InboundConnectionRequest(1));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
ASSERT_TRUE(channel.is_alive());
EXPECT_FALSE(dynamic_channel_closed);
EXPECT_EQ(kLocalId, channel->id());
EXPECT_EQ(kRemoteId, channel->remote_id());
// Test SDU transmission.
// SDU must have remote channel ID (unlike for fixed channels).
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, length 7)
0x01,
0x00,
0x08,
0x00,
// L2CAP B-frame: (length: 3, channel-id)
0x04,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
'T',
'e',
's',
't'),
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('T', 'e', 's', 't')));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
// Explicit deactivation should not res in |closed_cb| being called.
channel->Deactivate();
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
// clang-format off
ReceiveAclDataPacket(StaticByteBuffer(
// 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, length: 4, dst cid, src cid)
0x07, disconn_req_id, 0x04, 0x00,
LowerBits(kRemoteId), UpperBits(kRemoteId), LowerBits(kLocalId), UpperBits(kLocalId)));
// clang-format on
RunUntilIdle();
EXPECT_FALSE(dynamic_channel_closed);
}
TEST_F(ChannelManagerRealAclChannelTest, LinkSecurityProperties) {
sm::SecurityProperties security(sm::SecurityLevel::kEncrypted,
16,
/*secure_connections=*/false);
// Has no effect.
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
// Register a link and open a channel.
// The security properties should be accessible using the channel.
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
// The channel should start out at the lowest level of security.
EXPECT_EQ(sm::SecurityProperties(), fixed_channels.att->security());
// Assign a new security level.
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
// Channel should return the new security level.
EXPECT_EQ(security, fixed_channels.att->security());
}
TEST_F(ChannelManagerRealAclChannelTest,
AssignLinkSecurityPropertiesOnClosedLinkDoesNothing) {
// Register a link and open a channel.
// The security properties should be accessible using the channel.
LEFixedChannels fixed_channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ASSERT_TRUE(fixed_channels.att->Activate(NopRxCallback, DoNothing));
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
EXPECT_FALSE(fixed_channels.att.is_alive());
// Assign a new security level.
sm::SecurityProperties security(sm::SecurityLevel::kEncrypted,
16,
/*secure_connections=*/false);
chanmgr()->AssignLinkSecurityProperties(kTestHandle1, security);
}
TEST_F(ChannelManagerMockAclChannelTest, UpgradeSecurity) {
// The callback passed to to Channel::UpgradeSecurity().
sm::Result<> received_status = fit::ok();
int security_status_count = 0;
auto status_callback = [&](sm::Result<> status) {
received_status = status;
security_status_count++;
};
// The security handler callback assigned when registering a link.
sm::Result<> delivered_status = fit::ok();
sm::SecurityLevel last_requested_level = sm::SecurityLevel::kNoSecurity;
int security_request_count = 0;
auto security_handler = [&](hci_spec::ConnectionHandle handle,
sm::SecurityLevel level,
auto callback) {
EXPECT_EQ(kTestHandle1, handle);
last_requested_level = level;
security_request_count++;
callback(delivered_status);
};
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
DoNothing,
NopLeConnParamCallback,
std::move(security_handler));
l2cap::Channel::WeakPtr att = std::move(fixed_channels.att);
ASSERT_TRUE(att->Activate(NopRxCallback, DoNothing));
// Requesting security at or below the current level should succeed without
// doing anything.
att->UpgradeSecurity(sm::SecurityLevel::kNoSecurity, status_callback);
RunUntilIdle();
EXPECT_EQ(0, security_request_count);
EXPECT_EQ(1, security_status_count);
EXPECT_EQ(fit::ok(), received_status);
// Test reporting an error.
delivered_status = ToResult(HostError::kNotSupported);
att->UpgradeSecurity(sm::SecurityLevel::kEncrypted, status_callback);
RunUntilIdle();
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);
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
EXPECT_FALSE(att.is_alive());
EXPECT_EQ(1, security_request_count);
EXPECT_EQ(2, security_status_count);
}
TEST_F(ChannelManagerMockAclChannelTest, MtuOutboundChannelConfiguration) {
constexpr uint16_t kRemoteMtu = kDefaultMTU - 1;
constexpr uint16_t kLocalMtu = kMaxMTU;
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr activated_chan) {
channel = std::move(activated_chan);
};
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
// Signaling channel packets should be sent with high priority.
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
OutboundConfigurationResponse(kPeerConfigRequestId, kRemoteMtu),
kHighPriority);
ActivateOutboundChannel(
kTestPsm, kChannelParams, std::move(channel_cb), kTestHandle1);
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(
InboundConfigurationRequest(kPeerConfigRequestId, kRemoteMtu));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_EQ(kRemoteMtu, channel->max_tx_sdu_size());
EXPECT_EQ(kLocalMtu, channel->max_rx_sdu_size());
}
TEST_F(ChannelManagerMockAclChannelTest, MtuInboundChannelConfiguration) {
constexpr uint16_t kRemoteMtu = kDefaultMTU - 1;
constexpr uint16_t kLocalMtu = kMaxMTU;
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, DoNothing));
};
EXPECT_TRUE(chanmgr()->RegisterService(
kTestPsm, kChannelParams, std::move(channel_cb)));
CommandId kPeerConnectionRequestId = 3;
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionResponse(kPeerConnectionRequestId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
OutboundConfigurationResponse(kPeerConfigRequestId, kRemoteMtu),
kHighPriority);
ReceiveAclDataPacket(InboundConnectionRequest(kPeerConnectionRequestId));
ReceiveAclDataPacket(
InboundConfigurationRequest(kPeerConfigRequestId, kRemoteMtu));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_EQ(kRemoteMtu, channel->max_tx_sdu_size());
EXPECT_EQ(kLocalMtu, channel->max_rx_sdu_size());
}
TEST_F(ChannelManagerMockAclChannelTest,
OutboundChannelConfigurationUsesChannelParameters) {
l2cap::ChannelParameters chan_params;
auto config_mode =
l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission;
chan_params.mode = config_mode;
chan_params.max_rx_sdu_size = l2cap::kMinACLMTU;
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ReceiveAclDataPacket(testing::AclExtFeaturesInfoRsp(
cmd_ids.extended_features_id,
kTestHandle1,
kExtendedFeaturesBitEnhancedRetransmission));
Channel::WeakPtr channel;
auto channel_cb = [&channel](Channel::WeakPtr activated_chan) {
channel = std::move(activated_chan);
};
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(
OutboundConfigurationRequest(
config_req_id, *chan_params.max_rx_sdu_size, config_mode),
kHighPriority);
const auto kInboundMtu = kDefaultMTU;
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(
kPeerConfigRequestId, kInboundMtu, config_mode),
kHighPriority);
ActivateOutboundChannel(
kTestPsm, chan_params, std::move(channel_cb), kTestHandle1);
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(
kPeerConfigRequestId, kInboundMtu, config_mode));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_EQ(*chan_params.max_rx_sdu_size, channel->max_rx_sdu_size());
EXPECT_EQ(config_mode, channel->mode());
// Receiver Ready poll request should elicit a response if ERTM has been set
// up.
EXPECT_ACL_PACKET_OUT_(
testing::AclSFrameReceiverReady(kTestHandle1,
kRemoteId,
/*receive_seq_num=*/0,
/*is_poll_request=*/false,
/*is_poll_response=*/true),
kLowPriority);
ReceiveAclDataPacket(
testing::AclSFrameReceiverReady(kTestHandle1,
kLocalId,
/*receive_seq_num=*/0,
/*is_poll_request=*/true,
/*is_poll_response=*/false));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
}
TEST_F(ChannelManagerMockAclChannelTest,
InboundChannelConfigurationUsesChannelParameters) {
CommandId kPeerConnReqId = 3;
l2cap::ChannelParameters chan_params;
auto config_mode =
l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission;
chan_params.mode = config_mode;
chan_params.max_rx_sdu_size = l2cap::kMinACLMTU;
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
ReceiveAclDataPacket(testing::AclExtFeaturesInfoRsp(
cmd_ids.extended_features_id,
kTestHandle1,
kExtendedFeaturesBitEnhancedRetransmission));
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, DoNothing));
};
EXPECT_TRUE(
chanmgr()->RegisterService(kTestPsm, chan_params, std::move(channel_cb)));
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionResponse(kPeerConnReqId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(
OutboundConfigurationRequest(
config_req_id, *chan_params.max_rx_sdu_size, config_mode),
kHighPriority);
const auto kInboundMtu = kDefaultMTU;
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(
kPeerConfigRequestId, kInboundMtu, config_mode),
kHighPriority);
ReceiveAclDataPacket(InboundConnectionRequest(kPeerConnReqId));
ReceiveAclDataPacket(InboundConfigurationRequest(
kPeerConfigRequestId, kInboundMtu, config_mode));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_EQ(*chan_params.max_rx_sdu_size, channel->max_rx_sdu_size());
EXPECT_EQ(config_mode, channel->mode());
// Receiver Ready poll request should elicit a response if ERTM has been set
// up.
EXPECT_ACL_PACKET_OUT_(
testing::AclSFrameReceiverReady(kTestHandle1,
kRemoteId,
/*receive_seq_num=*/0,
/*is_poll_request=*/false,
/*is_poll_response=*/true),
kLowPriority);
ReceiveAclDataPacket(
testing::AclSFrameReceiverReady(kTestHandle1,
kLocalId,
/*receive_seq_num=*/0,
/*is_poll_request=*/true,
/*is_poll_response=*/false));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
}
TEST_F(ChannelManagerMockAclChannelTest,
UnregisteringUnknownHandleClearsPendingPacketsAndDoesNotCrash) {
// Packet for unregistered handle should be queued.
ReceiveAclDataPacket(
testing::AclConnectionReq(1, kTestHandle1, kRemoteId, kTestPsm));
chanmgr()->RemoveConnection(kTestHandle1);
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Since pending connection request packet was cleared, no response should be
// sent.
RunUntilIdle();
}
TEST_F(
ChannelManagerMockAclChannelTest,
PacketsReceivedAfterChannelDeactivatedAndBeforeRemoveChannelCalledAreDropped) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, DoNothing));
};
EXPECT_TRUE(chanmgr()->RegisterService(
kTestPsm, kChannelParams, std::move(channel_cb)));
CommandId kPeerConnectionRequestId = 3;
CommandId kLocalConfigRequestId = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionResponse(kPeerConnectionRequestId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(kLocalConfigRequestId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
ReceiveAclDataPacket(InboundConnectionRequest(kPeerConnectionRequestId));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(kLocalConfigRequestId));
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(channel.is_alive());
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
// channel marked inactive & LogicalLink::RemoveChannel called.
channel->Deactivate();
EXPECT_TRUE(AllExpectedPacketsSent());
StaticByteBuffer kPacket(
// ACL data header (handle: 0x0001, length: 4 bytes)
0x01,
0x00,
0x04,
0x00,
// L2CAP B-frame header (length: 0 bytes, channel-id)
0x00,
0x00,
LowerBits(kLocalId),
UpperBits(kLocalId));
// Packet for removed channel should be dropped by LogicalLink.
ReceiveAclDataPacket(kPacket);
}
TEST_F(ChannelManagerMockAclChannelTest,
ReceiveFixedChannelsInformationResponseWithNotSupportedResult) {
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Handler should check for res and not crash from reading mask or type.
ReceiveAclDataPacket(testing::AclNotSupportedInformationResponse(
cmd_ids.fixed_channels_supported_id, kTestHandle1));
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
ReceiveFixedChannelsInformationResponseWithInvalidResult) {
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Handler should check for res and not crash from reading mask or type.
StaticByteBuffer kPacket(
// ACL data header (handle: |link_handle|, length: 12 bytes)
LowerBits(kTestHandle1),
UpperBits(kTestHandle1),
0x0c,
0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08,
0x00,
0x01,
0x00,
// Information Response (type, ID, length: 4)
l2cap::kInformationResponse,
cmd_ids.fixed_channels_supported_id,
0x04,
0x00,
// Type = Fixed Channels Supported
LowerBits(
static_cast<uint16_t>(InformationType::kFixedChannelsSupported)),
UpperBits(
static_cast<uint16_t>(InformationType::kFixedChannelsSupported)),
// Invalid Result
0xFF,
0xFF);
ReceiveAclDataPacket(kPacket);
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
ReceiveFixedChannelsInformationResponseWithIncorrectType) {
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Handler should check type and not attempt to read fixed channel mask.
ReceiveAclDataPacket(testing::AclExtFeaturesInfoRsp(
cmd_ids.fixed_channels_supported_id, kTestHandle1, 0));
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
ReceiveFixedChannelsInformationResponseWithRejectStatus) {
const auto cmd_ids = QueueRegisterACL(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Handler should check status and not attempt to read fields.
ReceiveAclDataPacket(testing::AclCommandRejectNotUnderstoodRsp(
cmd_ids.fixed_channels_supported_id, kTestHandle1));
RunUntilIdle();
}
TEST_F(
ChannelManagerMockAclChannelTest,
ReceiveValidConnectionParameterUpdateRequestAsCentralAndRespondWithAcceptedResult) {
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
std::optional<hci_spec::LEPreferredConnectionParameters> params;
LEConnectionParameterUpdateCallback param_cb =
[&params](const hci_spec::LEPreferredConnectionParameters& cb_params) {
params = cb_params;
};
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
/*link_error_cb=*/DoNothing,
std::move(param_cb));
constexpr CommandId kParamReqId = 4; // random
EXPECT_LE_PACKET_OUT(testing::AclConnectionParameterUpdateRsp(
kParamReqId,
kTestHandle1,
ConnectionParameterUpdateResult::kAccepted),
kHighPriority);
ReceiveAclDataPacket(
testing::AclConnectionParameterUpdateReq(kParamReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult));
RunUntilIdle();
ASSERT_TRUE(params.has_value());
EXPECT_EQ(kIntervalMin, params->min_interval());
EXPECT_EQ(kIntervalMax, params->max_interval());
EXPECT_EQ(kPeripheralLatency, params->max_latency());
EXPECT_EQ(kTimeoutMult, params->supervision_timeout());
}
// If an LE Peripheral host receives a Connection Parameter Update Request, it
// should reject it.
TEST_F(
ChannelManagerMockAclChannelTest,
ReceiveValidConnectionParameterUpdateRequestAsPeripheralAndRespondWithReject) {
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
std::optional<hci_spec::LEPreferredConnectionParameters> params;
LEConnectionParameterUpdateCallback param_cb =
[&params](const hci_spec::LEPreferredConnectionParameters& cb_params) {
params = cb_params;
};
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::PERIPHERAL,
/*link_error_cb=*/DoNothing,
std::move(param_cb));
constexpr CommandId kParamReqId = 4; // random
EXPECT_LE_PACKET_OUT(testing::AclCommandRejectNotUnderstoodRsp(
kParamReqId, kTestHandle1, kLESignalingChannelId),
kHighPriority);
ReceiveAclDataPacket(
testing::AclConnectionParameterUpdateReq(kParamReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult));
RunUntilIdle();
ASSERT_FALSE(params.has_value());
}
TEST_F(
ChannelManagerMockAclChannelTest,
ReceiveInvalidConnectionParameterUpdateRequestsAndRespondWithRejectedResult) {
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
// Callback should not be called for request with invalid parameters.
LEConnectionParameterUpdateCallback param_cb = [](auto /*params*/) {
ADD_FAILURE();
};
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL,
/*link_error_cb=*/DoNothing,
std::move(param_cb));
constexpr CommandId kParamReqId = 4; // random
std::array invalid_requests = {
// interval min > interval max
testing::AclConnectionParameterUpdateReq(kParamReqId,
kTestHandle1,
/*interval_min=*/7,
/*interval_max=*/6,
kPeripheralLatency,
kTimeoutMult),
// interval_min too small
testing::AclConnectionParameterUpdateReq(
kParamReqId,
kTestHandle1,
hci_spec::kLEConnectionIntervalMin - 1,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult),
// interval max too large
testing::AclConnectionParameterUpdateReq(
kParamReqId,
kTestHandle1,
kIntervalMin,
hci_spec::kLEConnectionIntervalMax + 1,
kPeripheralLatency,
kTimeoutMult),
// latency too large
testing::AclConnectionParameterUpdateReq(
kParamReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
hci_spec::kLEConnectionLatencyMax + 1,
kTimeoutMult),
// timeout multiplier too small
testing::AclConnectionParameterUpdateReq(
kParamReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
hci_spec::kLEConnectionSupervisionTimeoutMin - 1),
// timeout multiplier too large
testing::AclConnectionParameterUpdateReq(
kParamReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
hci_spec::kLEConnectionSupervisionTimeoutMax + 1)};
for (auto& req : invalid_requests) {
EXPECT_LE_PACKET_OUT(testing::AclConnectionParameterUpdateRsp(
kParamReqId,
kTestHandle1,
ConnectionParameterUpdateResult::kRejected),
kHighPriority);
ReceiveAclDataPacket(req);
}
RunUntilIdle();
}
TEST_F(ChannelManagerMockAclChannelTest,
RequestConnParamUpdateForUnknownLinkIsNoOp) {
auto update_cb = [](auto) { ADD_FAILURE(); };
chanmgr()->RequestConnectionParameterUpdate(
kTestHandle1,
hci_spec::LEPreferredConnectionParameters(),
std::move(update_cb));
RunUntilIdle();
}
TEST_F(
ChannelManagerMockAclChannelTest,
RequestConnParamUpdateAsPeripheralAndReceiveAcceptedAndRejectedResponses) {
LEFixedChannels fixed_channels = RegisterLE(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::PERIPHERAL);
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
const hci_spec::LEPreferredConnectionParameters kParams(
kIntervalMin, kIntervalMax, kPeripheralLatency, kTimeoutMult);
std::optional<bool> accepted;
auto request_cb = [&accepted](bool cb_accepted) { accepted = cb_accepted; };
// Receive "Accepted" Response:
CommandId param_update_req_id = NextCommandId();
EXPECT_LE_PACKET_OUT(
testing::AclConnectionParameterUpdateReq(param_update_req_id,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult),
kHighPriority);
chanmgr()->RequestConnectionParameterUpdate(
kTestHandle1, kParams, request_cb);
RunUntilIdle();
EXPECT_FALSE(accepted.has_value());
ReceiveAclDataPacket(testing::AclConnectionParameterUpdateRsp(
param_update_req_id,
kTestHandle1,
ConnectionParameterUpdateResult::kAccepted));
RunUntilIdle();
ASSERT_TRUE(accepted.has_value());
EXPECT_TRUE(accepted.value());
accepted.reset();
// Receive "Rejected" Response:
param_update_req_id = NextCommandId();
EXPECT_LE_PACKET_OUT(
testing::AclConnectionParameterUpdateReq(param_update_req_id,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult),
kHighPriority);
chanmgr()->RequestConnectionParameterUpdate(
kTestHandle1, kParams, std::move(request_cb));
RunUntilIdle();
EXPECT_FALSE(accepted.has_value());
ReceiveAclDataPacket(testing::AclConnectionParameterUpdateRsp(
param_update_req_id,
kTestHandle1,
ConnectionParameterUpdateResult::kRejected));
RunUntilIdle();
ASSERT_TRUE(accepted.has_value());
EXPECT_FALSE(accepted.value());
}
TEST_F(ChannelManagerMockAclChannelTest, ConnParamUpdateRequestRejected) {
LEFixedChannels fixed_channels = RegisterLE(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::PERIPHERAL);
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
const hci_spec::LEPreferredConnectionParameters kParams(
kIntervalMin, kIntervalMax, kPeripheralLatency, kTimeoutMult);
std::optional<bool> accepted;
auto request_cb = [&accepted](bool cb_accepted) { accepted = cb_accepted; };
const CommandId kParamUpdateReqId = NextCommandId();
EXPECT_LE_PACKET_OUT(
testing::AclConnectionParameterUpdateReq(kParamUpdateReqId,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult),
kHighPriority);
chanmgr()->RequestConnectionParameterUpdate(
kTestHandle1, kParams, request_cb);
RunUntilIdle();
EXPECT_FALSE(accepted.has_value());
ReceiveAclDataPacket(testing::AclCommandRejectNotUnderstoodRsp(
kParamUpdateReqId, kTestHandle1, kLESignalingChannelId));
RunUntilIdle();
ASSERT_TRUE(accepted.has_value());
EXPECT_FALSE(accepted.value());
}
TEST_F(ChannelManagerRealAclChannelTest,
DestroyingChannelManagerReleasesLogicalLinkAndClosesChannels) {
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
auto link = chanmgr()->LogicalLinkForTesting(kTestHandle1);
ASSERT_TRUE(link.is_alive());
bool closed = false;
auto closed_cb = [&] { closed = true; };
auto chan = ActivateNewFixedChannel(
kConnectionlessChannelId, kTestHandle1, closed_cb);
ASSERT_TRUE(chan.is_alive());
ASSERT_FALSE(closed);
TearDown(); // Destroys channel manager
RunUntilIdle();
EXPECT_TRUE(closed);
// If link is still valid, there may be a memory leak.
EXPECT_FALSE(link.is_alive());
// If the above fails, check if the channel was holding a strong reference to
// the link.
chan = Channel::WeakPtr();
RunUntilIdle();
EXPECT_TRUE(closed);
EXPECT_FALSE(link.is_alive());
}
TEST_F(ChannelManagerMockAclChannelTest, RequestAclPriorityNormal) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&requested_priority](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kNormal, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 1u);
EXPECT_FALSE(requested_priority.has_value());
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
// Closing channel should not request normal priority because it is already
// the current priority.
channel->Deactivate();
EXPECT_EQ(result_cb_count, 1u);
EXPECT_FALSE(requested_priority.has_value());
}
TEST_F(ChannelManagerMockAclChannelTest, RequestAclPrioritySinkThenNormal) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&requested_priority](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 1u);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kSink);
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kSink);
channel->RequestAclPriority(AclPriority::kNormal, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 2u);
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kNormal);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kNormal);
requested_priority.reset();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
// Closing channel should not request normal priority because it is already
// the current priority.
channel->Deactivate();
EXPECT_FALSE(requested_priority.has_value());
}
TEST_F(ChannelManagerMockAclChannelTest,
RequestAclPrioritySinkThenDeactivateChannelAfterResult) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&requested_priority](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 1u);
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kSink);
requested_priority.reset();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
channel->Deactivate();
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kNormal);
}
TEST_F(ChannelManagerMockAclChannelTest,
RequestAclPrioritySinkThenReceiveDisconnectRequest) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&requested_priority](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 1u);
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kSink);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kSink);
requested_priority.reset();
const auto kPeerDisconReqId = 1;
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionResponse(kPeerDisconReqId),
kHighPriority);
ReceiveAclDataPacket(testing::AclDisconnectionReq(
kPeerDisconReqId, kTestHandle1, kRemoteId, kLocalId));
RunUntilIdle();
ASSERT_TRUE(requested_priority.has_value());
EXPECT_EQ(*requested_priority, AclPriority::kNormal);
}
TEST_F(
ChannelManagerMockAclChannelTest,
RequestAclPrioritySinkThenDeactivateChannelBeforeResultShouldResetPriorityOnDeactivate) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::vector<
std::pair<AclPriority, fit::callback<void(fit::result<fit::failed>)>>>
requests;
acl_data_channel()->set_request_acl_priority_cb(
[&](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requests.push_back({priority, std::move(cb)});
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kNormal);
EXPECT_EQ(result_cb_count, 0u);
EXPECT_EQ(requests.size(), 1u);
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
// Should queue kNormal ACL priority request.
channel->Deactivate();
ASSERT_EQ(requests.size(), 1u);
requests[0].second(fit::ok());
EXPECT_EQ(result_cb_count, 1u);
ASSERT_EQ(requests.size(), 2u);
EXPECT_EQ(requests[1].first, AclPriority::kNormal);
requests[1].second(fit::ok());
}
TEST_F(ChannelManagerMockAclChannelTest, RequestAclPrioritySinkFails) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
acl_data_channel()->set_request_acl_priority_cb(
[](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
cb(fit::failed());
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_TRUE(res.is_error());
result_cb_count++;
});
EXPECT_EQ(result_cb_count, 1u);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kNormal);
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
channel->Deactivate();
}
TEST_F(ChannelManagerMockAclChannelTest,
TwoChannelsRequestAclPrioritySinkAndDeactivate) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto kChannelIds0 = std::make_pair(ChannelId(0x40), ChannelId(0x41));
const auto kChannelIds1 = std::make_pair(ChannelId(0x41), ChannelId(0x42));
auto channel_0 =
SetUpOutboundChannel(kChannelIds0.first, kChannelIds0.second);
auto channel_1 =
SetUpOutboundChannel(kChannelIds1.first, kChannelIds1.second);
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel_0->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
ASSERT_TRUE(requested_priority);
EXPECT_EQ(*requested_priority, AclPriority::kSink);
EXPECT_EQ(result_cb_count, 1u);
EXPECT_EQ(channel_0->requested_acl_priority(), AclPriority::kSink);
requested_priority.reset();
channel_1->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
// Priority is already sink. No additional request should be sent.
EXPECT_FALSE(requested_priority);
EXPECT_EQ(result_cb_count, 2u);
EXPECT_EQ(channel_1->requested_acl_priority(), AclPriority::kSink);
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds0.first,
kChannelIds0.second),
kHighPriority);
channel_0->Deactivate();
// Because channel_1 is still using sink priority, no command should be sent.
EXPECT_FALSE(requested_priority);
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds1.first,
kChannelIds1.second),
kHighPriority);
channel_1->Deactivate();
ASSERT_TRUE(requested_priority);
EXPECT_EQ(*requested_priority, AclPriority::kNormal);
}
TEST_F(ChannelManagerMockAclChannelTest,
TwoChannelsRequestConflictingAclPriorities) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto kChannelIds0 = std::make_pair(ChannelId(0x40), ChannelId(0x41));
const auto kChannelIds1 = std::make_pair(ChannelId(0x41), ChannelId(0x42));
auto channel_0 =
SetUpOutboundChannel(kChannelIds0.first, kChannelIds0.second);
auto channel_1 =
SetUpOutboundChannel(kChannelIds1.first, kChannelIds1.second);
std::optional<AclPriority> requested_priority;
acl_data_channel()->set_request_acl_priority_cb(
[&](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requested_priority = priority;
cb(fit::ok());
});
size_t result_cb_count = 0;
channel_0->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
ASSERT_TRUE(requested_priority);
EXPECT_EQ(*requested_priority, AclPriority::kSink);
EXPECT_EQ(result_cb_count, 1u);
requested_priority.reset();
channel_1->RequestAclPriority(AclPriority::kSource, [&](auto res) {
EXPECT_TRUE(res.is_error());
result_cb_count++;
});
// Priority conflict should prevent priority request.
EXPECT_FALSE(requested_priority);
EXPECT_EQ(result_cb_count, 2u);
EXPECT_EQ(channel_1->requested_acl_priority(), AclPriority::kNormal);
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds0.first,
kChannelIds0.second),
kHighPriority);
channel_0->Deactivate();
ASSERT_TRUE(requested_priority);
EXPECT_EQ(*requested_priority, AclPriority::kNormal);
requested_priority.reset();
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds1.first,
kChannelIds1.second),
kHighPriority);
channel_1->Deactivate();
EXPECT_FALSE(requested_priority);
}
// If two channels request ACL priorities before the first command completes,
// they should receive responses as if they were handled strictly sequentially.
TEST_F(ChannelManagerMockAclChannelTest,
TwoChannelsRequestAclPrioritiesAtSameTime) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto kChannelIds0 = std::make_pair(ChannelId(0x40), ChannelId(0x41));
const auto kChannelIds1 = std::make_pair(ChannelId(0x41), ChannelId(0x42));
auto channel_0 =
SetUpOutboundChannel(kChannelIds0.first, kChannelIds0.second);
auto channel_1 =
SetUpOutboundChannel(kChannelIds1.first, kChannelIds1.second);
std::vector<fit::callback<void(fit::result<fit::failed>)>> command_callbacks;
acl_data_channel()->set_request_acl_priority_cb(
[&](auto priority, auto handle, auto cb) {
command_callbacks.push_back(std::move(cb));
});
size_t result_cb_count_0 = 0;
channel_0->RequestAclPriority(AclPriority::kSink,
[&](auto res) { result_cb_count_0++; });
EXPECT_EQ(command_callbacks.size(), 1u);
EXPECT_EQ(result_cb_count_0, 0u);
size_t result_cb_count_1 = 0;
channel_1->RequestAclPriority(AclPriority::kSource,
[&](auto res) { result_cb_count_1++; });
EXPECT_EQ(result_cb_count_1, 0u);
ASSERT_EQ(command_callbacks.size(), 1u);
command_callbacks[0](fit::ok());
EXPECT_EQ(result_cb_count_0, 1u);
// Second request should be notified of conflict error.
EXPECT_EQ(result_cb_count_1, 1u);
EXPECT_EQ(command_callbacks.size(), 1u);
// Because requests should be handled sequentially, the second request should
// have failed.
EXPECT_EQ(channel_0->requested_acl_priority(), AclPriority::kSink);
EXPECT_EQ(channel_1->requested_acl_priority(), AclPriority::kNormal);
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds0.first,
kChannelIds0.second),
kHighPriority);
channel_0->Deactivate();
EXPECT_ACL_PACKET_OUT_(testing::AclDisconnectionReq(NextCommandId(),
kTestHandle1,
kChannelIds1.first,
kChannelIds1.second),
kHighPriority);
channel_1->Deactivate();
}
TEST_F(ChannelManagerMockAclChannelTest,
QueuedSinkAclPriorityForClosedChannelIsIgnored) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
std::vector<
std::pair<AclPriority, fit::callback<void(fit::result<fit::failed>)>>>
requests;
acl_data_channel()->set_request_acl_priority_cb(
[&](auto priority, auto handle, auto cb) {
EXPECT_EQ(handle, kTestHandle1);
requests.push_back({priority, std::move(cb)});
});
size_t result_cb_count = 0;
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
ASSERT_EQ(requests.size(), 1u);
requests[0].second(fit::ok());
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kSink);
// Source request is queued and request is sent.
channel->RequestAclPriority(AclPriority::kSource, [&](auto res) {
EXPECT_EQ(fit::ok(), res);
result_cb_count++;
});
ASSERT_EQ(requests.size(), 2u);
EXPECT_EQ(result_cb_count, 1u);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kSink);
// Sink request is queued. It should receive an error since it is handled
// after the channel is closed.
channel->RequestAclPriority(AclPriority::kSink, [&](auto res) {
EXPECT_TRUE(res.is_error());
result_cb_count++;
});
ASSERT_EQ(requests.size(), 2u);
EXPECT_EQ(result_cb_count, 1u);
EXPECT_EQ(channel->requested_acl_priority(), AclPriority::kSink);
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(NextCommandId()),
kHighPriority);
// Closing channel will queue normal request.
channel->Deactivate();
EXPECT_FALSE(channel.is_alive());
// Send res to source request. Second sink request should receive error res
// too.
requests[1].second(fit::ok());
EXPECT_EQ(result_cb_count, 3u);
ASSERT_EQ(requests.size(), 3u);
EXPECT_EQ(requests[2].first, AclPriority::kNormal);
// Send response to kNormal request sent on Deactivate().
requests[2].second(fit::ok());
}
#ifndef NINSPECT
TEST_F(ChannelManagerMockAclChannelTest, InspectHierarchy) {
inspect::Inspector inspector;
chanmgr()->AttachInspect(inspector.GetRoot(), "l2cap");
chanmgr()->RegisterService(kSDP, kChannelParams, [](auto) {});
auto services_matcher =
AllOf(NodeMatches(NameMatches("services")),
ChildrenMatch(ElementsAre(NodeMatches(
AllOf(NameMatches("service_0x0"),
PropertyList(ElementsAre(StringIs("psm", "SDP"))))))));
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto conn_req_id = NextCommandId();
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionRequest(conn_req_id), kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
Channel::WeakPtr dynamic_channel;
auto channel_cb = [&dynamic_channel](l2cap::Channel::WeakPtr activated_chan) {
dynamic_channel = std::move(activated_chan);
};
ActivateOutboundChannel(
kTestPsm, kChannelParams, std::move(channel_cb), kTestHandle1, []() {});
ReceiveAclDataPacket(InboundConnectionResponse(conn_req_id));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
auto signaling_chan_matcher = NodeMatches(AllOf(
NameMatches("channel_0x2"),
PropertyList(UnorderedElementsAre(StringIs("local_id", "0x0001"),
StringIs("remote_id", "0x0001")))));
auto smp_chan_matcher = NodeMatches(AllOf(
NameMatches("channel_0x3"),
PropertyList(UnorderedElementsAre(StringIs("local_id", "0x0007"),
StringIs("remote_id", "0x0007")))));
auto dyn_chan_matcher = NodeMatches(
AllOf(NameMatches("channel_0x4"),
PropertyList(UnorderedElementsAre(StringIs("local_id", "0x0040"),
StringIs("remote_id", "0x9042"),
StringIs("psm", "SDP")))));
auto channels_matcher =
AllOf(NodeMatches(NameMatches("channels")),
ChildrenMatch(UnorderedElementsAre(
signaling_chan_matcher, smp_chan_matcher, dyn_chan_matcher)));
auto link_matcher = AllOf(
NodeMatches(NameMatches("logical_links")),
ChildrenMatch(ElementsAre(AllOf(
NodeMatches(AllOf(
NameMatches("logical_link_0x1"),
PropertyList(UnorderedElementsAre(
StringIs("handle", "0x0001"),
StringIs("link_type", "ACL"),
UintIs("flush_timeout_ms",
std::chrono::duration_cast<std::chrono::milliseconds>(
pw::chrono::SystemClock::duration::max())
.count()))))),
ChildrenMatch(ElementsAre(channels_matcher))))));
auto l2cap_node_matcher = AllOf(
NodeMatches(NameMatches("l2cap")),
ChildrenMatch(UnorderedElementsAre(link_matcher, services_matcher)));
auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo()).take_value();
EXPECT_THAT(hierarchy, ChildrenMatch(ElementsAre(l2cap_node_matcher)));
// inspector must outlive ChannelManager
chanmgr()->RemoveConnection(kTestHandle1);
}
#endif // NINSPECT
TEST_F(
ChannelManagerMockAclChannelTest,
OutboundChannelWithFlushTimeoutInChannelParametersAndDelayedFlushTimeoutCallback) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
EXPECT_CMD_PACKET_OUT(test_device(),
WriteAutomaticFlushTimeoutPacket(
kTestHandle1, kExpectedFlushTimeoutParam));
ChannelParameters chan_params;
chan_params.flush_timeout = kFlushTimeout;
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr activated_chan) {
channel = std::move(activated_chan);
};
SetUpOutboundChannelWithCallback(
kLocalId, kRemoteId, /*closed_cb=*/DoNothing, chan_params, channel_cb);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
// Channel should not be returned yet because setting flush timeout has not
// completed yet.
EXPECT_FALSE(channel.is_alive());
// Completing the command should cause the channel to be returned.
const auto kCommandComplete =
CommandCompletePacket(hci_spec::kWriteAutomaticFlushTimeout,
pw::bluetooth::emboss::StatusCode::SUCCESS);
test_device()->SendCommandChannelPacket(kCommandComplete);
RunUntilIdle();
ASSERT_TRUE(channel.is_alive());
ASSERT_TRUE(channel->info().flush_timeout.has_value());
EXPECT_EQ(channel->info().flush_timeout.value(), kFlushTimeout);
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, packet boundary
// flag: kFirstFlushable, length: 6)
0x01,
0x20,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(kRemoteId),
UpperBits(kRemoteId), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('h', 'i')));
}
TEST_F(ChannelManagerMockAclChannelTest,
OutboundChannelWithFlushTimeoutInChannelParametersFailure) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto kCommandCompleteError = CommandCompletePacket(
hci_spec::kWriteAutomaticFlushTimeout,
pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR);
EXPECT_CMD_PACKET_OUT(test_device(),
WriteAutomaticFlushTimeoutPacket(
kTestHandle1, kExpectedFlushTimeoutParam),
&kCommandCompleteError);
ChannelParameters chan_params;
chan_params.flush_timeout = kFlushTimeout;
auto channel = SetUpOutboundChannel(
kLocalId, kRemoteId, /*closed_cb=*/DoNothing, chan_params);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
// Flush timeout should not be set in channel info because setting a flush
// timeout failed.
EXPECT_FALSE(channel->info().flush_timeout.has_value());
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, packet boundary
// flag: kFirstNonFlushable, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(kRemoteId),
UpperBits(kRemoteId), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('h', 'i')));
}
TEST_F(ChannelManagerMockAclChannelTest,
InboundChannelWithFlushTimeoutInChannelParameters) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
const auto kCommandComplete =
CommandCompletePacket(hci_spec::kWriteAutomaticFlushTimeout,
pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
WriteAutomaticFlushTimeoutPacket(
kTestHandle1, kExpectedFlushTimeoutParam),
&kCommandComplete);
ChannelParameters chan_params;
chan_params.flush_timeout = kFlushTimeout;
Channel::WeakPtr channel;
auto channel_cb = [&channel](l2cap::Channel::WeakPtr opened_chan) {
channel = std::move(opened_chan);
EXPECT_TRUE(channel->Activate(NopRxCallback, DoNothing));
};
EXPECT_TRUE(
chanmgr()->RegisterService(kTestPsm, chan_params, std::move(channel_cb)));
CommandId kPeerConnectionRequestId = 3;
const auto config_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundConnectionResponse(kPeerConnectionRequestId),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationRequest(config_req_id),
kHighPriority);
EXPECT_ACL_PACKET_OUT_(OutboundConfigurationResponse(kPeerConfigRequestId),
kHighPriority);
ReceiveAclDataPacket(InboundConnectionRequest(kPeerConnectionRequestId));
ReceiveAclDataPacket(InboundConfigurationRequest(kPeerConfigRequestId));
ReceiveAclDataPacket(InboundConfigurationResponse(config_req_id));
RunUntilIdle();
EXPECT_TRUE(AllExpectedPacketsSent());
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
ASSERT_TRUE(channel.is_alive());
ASSERT_TRUE(channel->info().flush_timeout.has_value());
EXPECT_EQ(channel->info().flush_timeout.value(), kFlushTimeout);
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, packet boundary
// flag: kFirstFlushable, length: 6)
0x01,
0x20,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(kRemoteId),
UpperBits(kRemoteId), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('h', 'i')));
}
TEST_F(ChannelManagerMockAclChannelTest,
FlushableChannelAndNonFlushableChannelOnSameLink) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto nonflushable_channel = SetUpOutboundChannel();
auto flushable_channel = SetUpOutboundChannel(kLocalId + 1, kRemoteId + 1);
const auto kCommandComplete =
CommandCompletePacket(hci_spec::kWriteAutomaticFlushTimeout,
pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
WriteAutomaticFlushTimeoutPacket(
kTestHandle1, kExpectedFlushTimeoutParam),
&kCommandComplete);
flushable_channel->SetBrEdrAutomaticFlushTimeout(
kFlushTimeout, [](auto res) { EXPECT_EQ(fit::ok(), res); });
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
EXPECT_FALSE(nonflushable_channel->info().flush_timeout.has_value());
ASSERT_TRUE(flushable_channel->info().flush_timeout.has_value());
EXPECT_EQ(flushable_channel->info().flush_timeout.value(), kFlushTimeout);
EXPECT_ACL_PACKET_OUT_(
StaticByteBuffer(
// ACL data header (handle: 1, packet boundary flag: kFirstFlushable,
// length: 6)
0x01,
0x20,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(flushable_channel->remote_id()),
UpperBits(flushable_channel->remote_id()), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(flushable_channel->Send(NewBuffer('h', 'i')));
EXPECT_ACL_PACKET_OUT_(
StaticByteBuffer(
// ACL data header (handle: 1, packet boundary flag:
// kFirstNonFlushable, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(nonflushable_channel->remote_id()),
UpperBits(nonflushable_channel->remote_id()), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(nonflushable_channel->Send(NewBuffer('h', 'i')));
}
TEST_F(ChannelManagerMockAclChannelTest, SettingFlushTimeoutFails) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
auto channel = SetUpOutboundChannel();
const auto kCommandComplete = CommandCompletePacket(
hci_spec::kWriteAutomaticFlushTimeout,
pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID);
EXPECT_CMD_PACKET_OUT(test_device(),
WriteAutomaticFlushTimeoutPacket(
kTestHandle1, kExpectedFlushTimeoutParam),
&kCommandComplete);
channel->SetBrEdrAutomaticFlushTimeout(kFlushTimeout, [](auto res) {
EXPECT_EQ(
ToResult(pw::bluetooth::emboss::StatusCode::UNKNOWN_CONNECTION_ID),
res);
});
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
EXPECT_ACL_PACKET_OUT_(StaticByteBuffer(
// ACL data header (handle: 1, packet boundary
// flag: kFirstNonFlushable, length: 6)
0x01,
0x00,
0x06,
0x00,
// L2CAP B-frame
0x02,
0x00, // length: 2
LowerBits(kRemoteId),
UpperBits(kRemoteId), // remote id
'h',
'i'), // payload
kLowPriority);
EXPECT_TRUE(channel->Send(NewBuffer('h', 'i')));
}
TEST_F(ChannelManagerMockAclChannelTest, StartAndStopA2dpOffloadSuccess) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
A2dpOffloadManager::Configuration config = BuildConfiguration();
Channel::WeakPtr channel = SetUpOutboundChannel();
const auto command_complete =
CommandCompletePacket(android_hci::kA2dpOffloadCommand,
pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
StartA2dpOffloadRequest(config,
channel->link_handle(),
channel->remote_id(),
channel->max_tx_sdu_size()),
&command_complete);
std::optional<hci::Result<>> result;
channel->StartA2dpOffload(config, [&result](auto res) {
EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
result = res;
});
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->is_ok());
EXPECT_CMD_PACKET_OUT(
test_device(), StopA2dpOffloadRequest(), &command_complete);
result.reset();
channel->StopA2dpOffload([&result](auto res) {
EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
result = res;
});
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->is_ok());
EXPECT_CMD_PACKET_OUT(test_device(),
StartA2dpOffloadRequest(config,
channel->link_handle(),
channel->remote_id(),
channel->max_tx_sdu_size()),
&command_complete);
result.reset();
channel->StartA2dpOffload(config, [&result](auto res) {
EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
result = res;
});
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->is_ok());
// Stop A2DP offload command sent on channel destruction
EXPECT_CMD_PACKET_OUT(
test_device(), StopA2dpOffloadRequest(), &command_complete);
}
TEST_F(ChannelManagerMockAclChannelTest,
DisconnectChannelAfterA2dpOffloadStarted) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
A2dpOffloadManager::Configuration config = BuildConfiguration();
Channel::WeakPtr channel = SetUpOutboundChannel();
const auto command_complete =
CommandCompletePacket(android_hci::kA2dpOffloadCommand,
pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
StartA2dpOffloadRequest(config,
channel->link_handle(),
channel->remote_id(),
channel->max_tx_sdu_size()),
&command_complete);
std::optional<hci::Result<>> result;
channel->StartA2dpOffload(config, [&result](auto res) {
EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
result = res;
});
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->is_ok());
ASSERT_TRUE(channel.is_alive());
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
// Stop A2DP offload command sent on channel close
EXPECT_CMD_PACKET_OUT(
test_device(), StopA2dpOffloadRequest(), &command_complete);
channel->Deactivate();
ASSERT_FALSE(channel.is_alive());
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
}
TEST_F(ChannelManagerMockAclChannelTest,
DisconnectChannelWhileA2dpOffloadStarting) {
QueueRegisterACL(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::CENTRAL);
RunUntilIdle();
A2dpOffloadManager::Configuration config = BuildConfiguration();
Channel::WeakPtr channel = SetUpOutboundChannel();
const auto command_complete =
CommandCompletePacket(android_hci::kA2dpOffloadCommand,
pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(),
StartA2dpOffloadRequest(config,
channel->link_handle(),
channel->remote_id(),
channel->max_tx_sdu_size()),
&command_complete);
std::optional<hci::Result<>> result;
channel->StartA2dpOffload(config, [&result](auto res) {
EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::SUCCESS), res);
result = res;
});
EXPECT_FALSE(result.has_value());
ASSERT_TRUE(channel.is_alive());
const auto disconn_req_id = NextCommandId();
EXPECT_ACL_PACKET_OUT_(OutboundDisconnectionRequest(disconn_req_id),
kHighPriority);
// Stop A2DP offload command sent on channel close
EXPECT_CMD_PACKET_OUT(
test_device(), StopA2dpOffloadRequest(), &command_complete);
channel->Deactivate();
ASSERT_FALSE(channel.is_alive());
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedCommandPacketsSent());
}
TEST_F(ChannelManagerMockAclChannelTest,
SignalLinkErrorStopsDeliveryOfBufferedRxPackets) {
// LE-U link
LEFixedChannels fixed_channels =
RegisterLE(kTestHandle1, pw::bluetooth::emboss::ConnectionRole::CENTRAL);
// Queue 2 packets to be delivers on channel activation.
StaticByteBuffer payload_0(0x00);
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00, // connection handle + flags
0x05,
0x00, // Length
// L2CAP B-frame
0x01,
0x00, // Length
LowerBits(kATTChannelId),
UpperBits(kATTChannelId),
// Payload
payload_0[0]));
ReceiveAclDataPacket(StaticByteBuffer(
// ACL data header (starting fragment)
0x01,
0x00, // connection handle + flags
0x05,
0x00, // Length
// L2CAP B-frame
0x01,
0x00, // Length
LowerBits(kATTChannelId),
UpperBits(kATTChannelId),
// Payload
0x01));
RunUntilIdle();
bool closed_called = false;
auto closed_cb = [&closed_called] { closed_called = true; };
int rx_count = 0;
auto rx_callback = [&](ByteBufferPtr payload) {
rx_count++;
if (rx_count == 1) {
EXPECT_THAT(*payload, BufferEq(payload_0));
// This should stop delivery of the second packet.
fixed_channels.att->SignalLinkError();
return;
}
};
ASSERT_TRUE(fixed_channels.att->Activate(std::move(rx_callback), closed_cb));
RunUntilIdle();
EXPECT_EQ(rx_count, 1);
EXPECT_TRUE(closed_called);
// Ensure the link is removed.
chanmgr()->RemoveConnection(kTestHandle1);
RunUntilIdle();
}
TEST_F(ChannelManagerRealAclChannelTest,
InboundRfcommChannelFailsWithPsmNotSupported) {
constexpr l2cap::Psm kPsm = l2cap::kRFCOMM;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
const l2cap::CommandId kPeerConnReqId = 1;
// Incoming connection refused, RFCOMM is not routed.
EXPECT_ACL_PACKET_OUT(test_device(),
l2cap::testing::AclConnectionRsp(
kPeerConnReqId,
kTestHandle1,
kRemoteId,
/*dst_id=*/0x0000,
l2cap::ConnectionResult::kPsmNotSupported));
test_device()->SendACLDataChannelPacket(l2cap::testing::AclConnectionReq(
kPeerConnReqId, kTestHandle1, kRemoteId, kPsm));
RunUntilIdle();
}
TEST_F(ChannelManagerRealAclChannelTest,
InboundPacketQueuedAfterChannelOpenIsNotDropped) {
constexpr l2cap::Psm kPsm = l2cap::kSDP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
chanmgr()->RegisterService(kPsm, kChannelParameters, std::move(chan_cb));
RunUntilIdle();
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, kTestHandle1, kRemoteId, kLocalId));
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclConfigReq(
kConfigReqId, kTestHandle1, kRemoteId, kChannelParameters));
test_device()->SendACLDataChannelPacket(l2cap::testing::AclConnectionReq(
kConnectionReqId, kTestHandle1, kRemoteId, kPsm));
// Config negotiation will not complete yet.
RunUntilIdle();
// Remaining config negotiation will be added to dispatch loop.
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclConfigRsp(
kPeerConfigReqId, kTestHandle1, kRemoteId, kChannelParameters));
test_device()->SendACLDataChannelPacket(l2cap::testing::AclConfigReq(
kPeerConfigReqId, kTestHandle1, kLocalId, kChannelParameters));
test_device()->SendACLDataChannelPacket(l2cap::testing::AclConfigRsp(
kConfigReqId, kTestHandle1, kLocalId, kChannelParameters));
// Queue up a data packet for the new channel before the channel configuration
// has been processed.
ASSERT_FALSE(channel.is_alive());
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,
0xf0,
0x9f,
0x94,
0xb0));
// Run until the channel opens and the packet is written to the socket buffer.
RunUntilIdle();
ASSERT_TRUE(channel.is_alive());
std::vector<ByteBufferPtr> rx_packets;
auto rx_cb = [&rx_packets](ByteBufferPtr sdu) {
rx_packets.push_back(std::move(sdu));
};
ASSERT_TRUE(channel->Activate(rx_cb, DoNothing));
RunUntilIdle();
ASSERT_EQ(rx_packets.size(), 1u);
ASSERT_EQ(rx_packets[0]->size(), 4u);
EXPECT_EQ("🔰", rx_packets[0]->view(0, 4u).AsString());
}
TEST_F(ChannelManagerRealAclChannelTest,
NegotiateChannelParametersOnOutboundL2capChannel) {
constexpr l2cap::Psm kPsm = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr uint16_t kMtu = l2cap::kMinACLMTU;
l2cap::ChannelParameters chan_params;
chan_params.mode =
l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission;
chan_params.max_rx_sdu_size = kMtu;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(kTestHandle1,
kPsm,
kLocalId,
kRemoteId,
chan_cb,
chan_params,
chan_params);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(channel.is_alive());
EXPECT_EQ(kTestHandle1, channel->link_handle());
EXPECT_EQ(*chan_params.max_rx_sdu_size, channel->max_rx_sdu_size());
EXPECT_EQ(*chan_params.mode, channel->mode());
}
TEST_F(ChannelManagerRealAclChannelTest,
NegotiateChannelParametersOnInboundChannel) {
constexpr l2cap::Psm kPsm = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
l2cap::ChannelParameters chan_params;
chan_params.mode =
l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission;
chan_params.max_rx_sdu_size = l2cap::kMinACLMTU;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
chanmgr()->RegisterService(kPsm, chan_params, chan_cb);
QueueInboundL2capConnection(
kTestHandle1, kPsm, kLocalId, kRemoteId, chan_params, chan_params);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(channel.is_alive());
EXPECT_EQ(*chan_params.max_rx_sdu_size, channel->max_rx_sdu_size());
EXPECT_EQ(*chan_params.mode, channel->mode());
}
TEST_F(ChannelManagerRealAclChannelTest,
RequestConnectionParameterUpdateAndReceiveResponse) {
// Valid parameter values
constexpr uint16_t kIntervalMin = 6;
constexpr uint16_t kIntervalMax = 7;
constexpr uint16_t kPeripheralLatency = 1;
constexpr uint16_t kTimeoutMult = 10;
const hci_spec::LEPreferredConnectionParameters kParams(
kIntervalMin, kIntervalMax, kPeripheralLatency, kTimeoutMult);
QueueLEConnection(kTestHandle1,
pw::bluetooth::emboss::ConnectionRole::PERIPHERAL);
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,
kTestHandle1,
kIntervalMin,
kIntervalMax,
kPeripheralLatency,
kTimeoutMult));
chanmgr()->RequestConnectionParameterUpdate(
kTestHandle1, kParams, request_cb);
RunUntilIdle();
EXPECT_FALSE(accepted.has_value());
test_device()->SendACLDataChannelPacket(
l2cap::testing::AclConnectionParameterUpdateRsp(
param_update_req_id,
kTestHandle1,
l2cap::ConnectionParameterUpdateResult::kAccepted));
RunUntilIdle();
ASSERT_TRUE(accepted.has_value());
EXPECT_TRUE(accepted.value());
accepted.reset();
}
TEST_F(ChannelManagerRealAclChannelTest, AddLEConnectionReturnsFixedChannels) {
auto channels = QueueLEConnection(
kTestHandle1, pw::bluetooth::emboss::ConnectionRole::PERIPHERAL);
ASSERT_TRUE(channels.att.is_alive());
EXPECT_EQ(l2cap::kATTChannelId, channels.att->id());
ASSERT_TRUE(channels.smp.is_alive());
EXPECT_EQ(l2cap::kLESMPChannelId, channels.smp->id());
}
TEST_F(ChannelManagerRealAclChannelTest,
OutboundChannelIsInvalidWhenL2capFailsToOpenChannel) {
constexpr l2cap::Psm kPsm = l2cap::kAVCTP;
// 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.is_alive());
};
chanmgr()->OpenL2capChannel(
kTestHandle1, kPsm, kChannelParameters, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(chan_cb_called);
}
TEST_F(ChannelManagerRealAclChannelTest, SignalingChannelAndOneDynamicChannel) {
constexpr l2cap::Psm kPsm = l2cap::kSDP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
// L2CAP connection request/response, config request, config response
constexpr size_t kChannelCreationPacketCount = 3;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
l2cap::Channel::WeakPtr channel;
auto chan_cb = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel.is_alive());
channel->Activate(NopRxCallback, DoNothing);
// Free up the buffer space from packets sent while creating |channel|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1,
kConnectionCreationPacketCount + kChannelCreationPacketCount));
// Fill up BR/EDR controller buffer then queue one additional packet
for (size_t i = 0; i <= kBufferMaxNumPackets; i++) {
// Last packet should remain queued
if (i < kBufferMaxNumPackets) {
const StaticByteBuffer kPacket(
// ACL data header (handle: 0, length 1)
0x01,
0x00,
0x05,
0x00,
// L2CAP B-frame: (length: 1, channel-id)
0x01,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
// L2CAP payload
0x01);
EXPECT_ACL_PACKET_OUT(test_device(), kPacket);
}
// Create PDU to send on dynamic channel
EXPECT_TRUE(channel->Send(NewBuffer(0x01)));
RunUntilIdle();
}
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
// Send out last packet
EXPECT_ACL_PACKET_OUT(test_device(),
StaticByteBuffer(
// ACL data header (handle: 0, length 1)
0x01,
0x00,
0x05,
0x00,
// L2CAP B-frame: (length: 4, channel-id)
0x01,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
// L2CAP payload
0x01));
test_device()->SendCommandChannelPacket(
bt::testing::NumberOfCompletedPacketsPacket(kTestHandle1, 1));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
}
TEST_F(ChannelManagerRealAclChannelTest,
SignalingChannelAndTwoDynamicChannels) {
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/response, config request, config response
constexpr size_t kChannelCreationPacketCount = 3;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
l2cap::Channel::WeakPtr channel0;
auto chan_cb0 = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel0 = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm0, kLocalId0, kRemoteId0, std::move(chan_cb0));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
ASSERT_TRUE(channel0.is_alive());
ASSERT_TRUE(channel0->Activate(NopRxCallback, DoNothing));
l2cap::Channel::WeakPtr channel1;
auto chan_cb1 = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel1 = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm1, kLocalId1, kRemoteId1, std::move(chan_cb1));
// Free up the buffer space from packets sent while creating |channel0|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1, kChannelCreationPacketCount));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel1.is_alive());
ASSERT_TRUE(channel1->Activate(NopRxCallback, DoNothing));
// Free up the buffer space from packets sent while creating |channel1|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1,
kConnectionCreationPacketCount + kChannelCreationPacketCount));
RunUntilIdle();
// Queue size should be equal to or larger than |num_queued_packets| to ensure
// that all packets get queued and sent
uint16_t num_queued_packets = 5;
EXPECT_TRUE(num_queued_packets <= kDefaultTxMaxQueuedCount);
// Queue 15 packets in total, distributed between the two channels
// Fill up BR/EDR controller buffer then queue 5 additional packets
for (size_t i = 0; i < kBufferMaxNumPackets + num_queued_packets; i++) {
Channel::WeakPtr channel = (i % 2) ? channel1 : channel0;
ChannelId channel_id = (i % 2) ? kRemoteId1 : kRemoteId0;
const StaticByteBuffer kPacket(
// ACL data header (handle: 1, length 5)
0x01,
0x00,
0x05,
0x00,
// L2CAP B-frame: (length: 1, channel_id)
0x01,
0x00,
LowerBits(channel_id),
UpperBits(channel_id),
// L2CAP payload
0x01);
EXPECT_ACL_PACKET_OUT(test_device(), kPacket);
// Create PDU to send on dynamic channel
EXPECT_TRUE(channel->Send(NewBuffer(0x01)));
RunUntilIdle();
}
EXPECT_FALSE(test_device()->AllExpectedDataPacketsSent());
// Notify the processed packets with a Number Of Completed Packet HCI event
// This should cause the remaining 5 packets to be sent
test_device()->SendCommandChannelPacket(
NumberOfCompletedPacketsPacket(kTestHandle1, 5));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
}
TEST_F(ChannelManagerRealAclChannelTest, ChannelMaximumQueueSize) {
constexpr l2cap::Psm kPsm = l2cap::kSDP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
// L2CAP connection request/response, config request, config response
constexpr size_t kChannelCreationPacketCount = 3;
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
l2cap::Channel::WeakPtr channel;
auto chan_cb = [&](auto activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
EXPECT_TRUE(channel.is_alive());
channel->Activate(NopRxCallback, DoNothing);
// Free up the buffer space from packets sent while creating |channel|
test_device()->SendCommandChannelPacket(NumberOfCompletedPacketsPacket(
kTestHandle1,
kConnectionCreationPacketCount + kChannelCreationPacketCount));
// Set maximum PDU queue size for |channel|. Queue size should be less than
// |num_queued_packets| to ensure that some packets are dropped in order to
// test maximum queue size behaviour
const uint16_t kTxMaxQueuedCount = 10;
channel->set_max_tx_queued(kTxMaxQueuedCount);
uint16_t num_queued_packets = 20;
// Fill up BR/EDR controller buffer then queue 20 additional packets. 10 of
// these packets will be dropped since the maximum PDU queue size
// |kTxMaxQueuedCount| is 10
for (size_t i = 0; i < kBufferMaxNumPackets + num_queued_packets; i++) {
if (i < kBufferMaxNumPackets + channel->max_tx_queued()) {
const StaticByteBuffer kPacket(
// ACL data header (handle: 0, length 1)
0x01,
0x00,
0x05,
0x00,
// L2CAP B-frame: (length: 1, channel-id)
0x01,
0x00,
LowerBits(kRemoteId),
UpperBits(kRemoteId),
// L2CAP payload
0x01);
EXPECT_ACL_PACKET_OUT(test_device(), kPacket);
}
// Create PDU to send on dynamic channel
EXPECT_TRUE(channel->Send(NewBuffer(0x01)));
RunUntilIdle();
}
EXPECT_FALSE(test_device()->AllExpectedDataPacketsSent());
// Notify the processed packets with a Number Of Completed Packet HCI event
// This should cause the remaining 10 packets to be sent
test_device()->SendCommandChannelPacket(
bt::testing::NumberOfCompletedPacketsPacket(kTestHandle1, 10));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
}
class AclPriorityTest
: public ChannelManagerRealAclChannelTest,
public ::testing::WithParamInterface<std::pair<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_spec::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;
std::optional<hci_spec::ConnectionHandle> connection_handle_from_encode_cb;
std::optional<AclPriority> priority_from_encode_cb;
test_device()->set_encode_vendor_command_cb(
[&](pw::bluetooth::VendorCommandParameters vendor_params,
fit::callback<void(pw::Result<pw::span<const std::byte>>)> callback) {
ASSERT_TRUE(
std::holds_alternative<
pw::bluetooth::SetAclPriorityCommandParameters>(vendor_params));
auto& params = std::get<pw::bluetooth::SetAclPriorityCommandParameters>(
vendor_params);
connection_handle_from_encode_cb = params.connection_handle;
priority_from_encode_cb = params.priority;
callback(kEncodedCommand.subspan());
});
QueueAclConnection(kTestHandle1);
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
Channel::WeakPtr channel;
auto chan_cb = [&](l2cap::Channel::WeakPtr activated_chan) {
EXPECT_EQ(kTestHandle1, activated_chan->link_handle());
channel = std::move(activated_chan);
};
QueueOutboundL2capConnection(
kTestHandle1, kPsm, kLocalId, kRemoteId, std::move(chan_cb));
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
// We should have opened a channel successfully.
ASSERT_TRUE(channel.is_alive());
channel->Activate([](auto) {}, []() {});
if (kPriority != AclPriority::kNormal) {
auto cmd_complete = CommandCompletePacket(
op_code,
kExpectSuccess ? pw::bluetooth::emboss::StatusCode::SUCCESS
: pw::bluetooth::emboss::StatusCode::UNKNOWN_COMMAND);
EXPECT_CMD_PACKET_OUT(test_device(), kEncodedCommand, &cmd_complete);
}
size_t request_cb_count = 0;
channel->RequestAclPriority(kPriority, [&](auto res) {
request_cb_count++;
EXPECT_EQ(kExpectSuccess, res.is_ok());
});
RunUntilIdle();
EXPECT_EQ(request_cb_count, 1u);
if (kPriority == AclPriority::kNormal) {
EXPECT_FALSE(connection_handle_from_encode_cb);
} else {
ASSERT_TRUE(connection_handle_from_encode_cb);
EXPECT_EQ(connection_handle_from_encode_cb.value(), kTestHandle1);
ASSERT_TRUE(priority_from_encode_cb);
EXPECT_EQ(priority_from_encode_cb.value(), kPriority);
}
connection_handle_from_encode_cb.reset();
priority_from_encode_cb.reset();
if (kPriority != AclPriority::kNormal && kExpectSuccess) {
auto cmd_complete = CommandCompletePacket(
op_code, pw::bluetooth::emboss::StatusCode::SUCCESS);
EXPECT_CMD_PACKET_OUT(test_device(), kEncodedCommand, &cmd_complete);
}
EXPECT_ACL_PACKET_OUT(
test_device(),
l2cap::testing::AclDisconnectionReq(
NextCommandId(), kTestHandle1, kLocalId, kRemoteId));
// Deactivating channel should send priority command to revert priority back
// to normal if it was changed.
channel->Deactivate();
RunUntilIdle();
EXPECT_TRUE(test_device()->AllExpectedDataPacketsSent());
if (kPriority != AclPriority::kNormal && kExpectSuccess) {
ASSERT_TRUE(connection_handle_from_encode_cb);
EXPECT_EQ(connection_handle_from_encode_cb.value(), kTestHandle1);
ASSERT_TRUE(priority_from_encode_cb);
EXPECT_EQ(priority_from_encode_cb.value(), AclPriority::kNormal);
} else {
EXPECT_FALSE(connection_handle_from_encode_cb);
}
}
const std::array<std::pair<AclPriority, bool>, 4> kPriorityParams = {
{{AclPriority::kSource, false},
{AclPriority::kSource, true},
{AclPriority::kSink, true},
{AclPriority::kNormal, true}}};
INSTANTIATE_TEST_SUITE_P(ChannelManagerMockControllerTest,
AclPriorityTest,
::testing::ValuesIn(kPriorityParams));
#ifndef NINSPECT
TEST_F(ChannelManagerRealAclChannelTest, InspectHierarchy) {
inspect::Inspector inspector;
chanmgr()->AttachInspect(inspector.GetRoot(),
ChannelManager::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))));
}
#endif // NINSPECT
} // namespace
} // namespace bt::l2cap