blob: 3d533b67b3d78be6c4a6554ba14898ef5f8b4453 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/l2cap/bredr_command_handler.h"
#include <lib/async/cpp/task.h>
#include <memory>
#include <unordered_map>
#include <vector>
#include "lib/gtest/test_loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel_configuration.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_signaling_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
namespace bt::l2cap::internal {
namespace {
using TestBase = ::gtest::TestLoopFixture;
constexpr uint16_t kPsm = 0x0001;
constexpr ChannelId kLocalCId = 0x0040;
constexpr ChannelId kRemoteCId = 0x60a3;
class L2CAP_BrEdrCommandHandlerTest : public TestBase {
public:
L2CAP_BrEdrCommandHandlerTest() = default;
~L2CAP_BrEdrCommandHandlerTest() override = default;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_BrEdrCommandHandlerTest);
protected:
// TestLoopFixture overrides
void SetUp() override {
TestBase::SetUp();
signaling_channel_ = std::make_unique<testing::FakeSignalingChannel>(dispatcher());
command_handler_ = std::make_unique<BrEdrCommandHandler>(
fake_sig(), fit::bind_member(this, &L2CAP_BrEdrCommandHandlerTest::OnRequestFail));
request_fail_callback_ = nullptr;
failed_requests_ = 0;
}
void TearDown() override {
request_fail_callback_ = nullptr;
signaling_channel_ = nullptr;
command_handler_ = nullptr;
TestBase::TearDown();
}
testing::FakeSignalingChannel* fake_sig() const { return signaling_channel_.get(); }
BrEdrCommandHandler* cmd_handler() const { return command_handler_.get(); }
size_t failed_requests() const { return failed_requests_; }
void set_request_fail_callback(fit::closure request_fail_callback) {
ZX_ASSERT(!request_fail_callback_);
request_fail_callback_ = std::move(request_fail_callback);
}
private:
void OnRequestFail() {
failed_requests_++;
if (request_fail_callback_) {
request_fail_callback_();
}
}
std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
std::unique_ptr<BrEdrCommandHandler> command_handler_;
fit::closure request_fail_callback_;
size_t failed_requests_;
};
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRej) {
constexpr ChannelId kBadLocalCId = 0x0005; // Not a dynamic channel
// Connection Request payload
auto expected_conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
// Command Reject payload
auto rej_rsp = CreateStaticByteBuffer(
// Reject Reason (invalid channel ID)
LowerBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
UpperBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
// Local (relative to rejecter) CID
LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
// Remote (relative to rejecter) CID
LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
{SignalingChannel::Status::kReject, rej_rsp.view()});
bool cb_called = false;
auto on_conn_rsp = [&cb_called,
kBadLocalCId](const BrEdrCommandHandler::ConnectionResponse& rsp) {
cb_called = true;
EXPECT_EQ(BrEdrCommandHandler::Status::kReject, rsp.status());
EXPECT_EQ(kInvalidChannelId, rsp.remote_cid());
EXPECT_EQ(kBadLocalCId, rsp.local_cid());
return BrEdrCommandHandler::ResponseHandlerAction::kCompleteOutboundTransaction;
};
EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kBadLocalCId, std::move(on_conn_rsp)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRejNotEnoughBytesInRejection) {
constexpr ChannelId kBadLocalCId = 0x0005; // Not a dynamic channel
// Connection Request payload
auto expected_conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
// Command Reject payload (the invalid channel IDs are missing)
auto rej_rsp = CreateStaticByteBuffer(
// Reject Reason (invalid channel ID)
LowerBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
UpperBits(static_cast<uint16_t>(RejectReason::kInvalidCID)));
EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
{SignalingChannel::Status::kReject, rej_rsp.view()});
bool cb_called = false;
auto on_conn_rsp = [&cb_called](const BrEdrCommandHandler::ConnectionResponse& rsp) {
cb_called = true;
return BrEdrCommandHandler::ResponseHandlerAction::kCompleteOutboundTransaction;
};
EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kBadLocalCId, std::move(on_conn_rsp)));
RunLoopUntilIdle();
EXPECT_FALSE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspOk) {
// Connection Request payload
auto expected_conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
// Connection Response payload
auto ok_conn_rsp = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Successful)
0x00, 0x00,
// Status (No further information available)
0x00, 0x00);
EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
{SignalingChannel::Status::kSuccess, ok_conn_rsp.view()});
bool cb_called = false;
auto on_conn_rsp = [&cb_called](const BrEdrCommandHandler::ConnectionResponse& rsp) {
cb_called = true;
EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
EXPECT_EQ(kRemoteCId, rsp.remote_cid());
EXPECT_EQ(kLocalCId, rsp.local_cid());
EXPECT_EQ(ConnectionResult::kSuccess, rsp.result());
EXPECT_EQ(ConnectionStatus::kNoInfoAvailable, rsp.conn_status());
return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
};
EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspPendingAuthThenOk) {
// Connection Request payload
auto expected_conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
// Connection Response payload
auto pend_conn_rsp = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Pending)
0x01, 0x00,
// Status (Authorization pending)
0x02, 0x00);
auto ok_conn_rsp = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Successful)
0x00, 0x00,
// Status (No further information available)
0x00, 0x00);
EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
{SignalingChannel::Status::kSuccess, pend_conn_rsp.view()},
{SignalingChannel::Status::kSuccess, ok_conn_rsp.view()});
int cb_count = 0;
auto on_conn_rsp = [&cb_count](const BrEdrCommandHandler::ConnectionResponse& rsp) {
cb_count++;
EXPECT_EQ(kRemoteCId, rsp.remote_cid());
EXPECT_EQ(kLocalCId, rsp.local_cid());
if (cb_count == 1) {
EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
EXPECT_EQ(ConnectionResult::kPending, rsp.result());
EXPECT_EQ(ConnectionStatus::kAuthorizationPending, rsp.conn_status());
return SignalingChannel::ResponseHandlerAction::kExpectAdditionalResponse;
} else if (cb_count == 2) {
EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
EXPECT_EQ(ConnectionResult::kSuccess, rsp.result());
EXPECT_EQ(ConnectionStatus::kNoInfoAvailable, rsp.conn_status());
}
return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
};
EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
RunLoopUntilIdle();
EXPECT_EQ(2, cb_count);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspTimeOut) {
// Connection Request payload
auto expected_conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
{SignalingChannel::Status::kTimeOut, {}});
ASSERT_EQ(0u, failed_requests());
auto on_conn_rsp = [](auto&) {
ADD_FAILURE();
return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
};
EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1u, failed_requests());
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspNotSupported) {
BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
EXPECT_EQ(InformationType::kConnectionlessMTU, type);
responder->SendNotSupported();
};
cmd_handler()->ServeInformationRequest(std::move(cb));
// Information Request payload
auto info_req = CreateStaticByteBuffer(
// Type = Connectionless MTU
0x01, 0x00);
// Information Response payload
auto expected_rsp = CreateStaticByteBuffer(
// Type = Connectionless MTU
0x01, 0x00,
// Result = Not supported
0x01, 0x00);
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspConnlessMtu) {
BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
EXPECT_EQ(InformationType::kConnectionlessMTU, type);
responder->SendConnectionlessMtu(0x02dc);
};
cmd_handler()->ServeInformationRequest(std::move(cb));
// Information Request payload
auto info_req = CreateStaticByteBuffer(
// Type = Connectionless MTU
0x01, 0x00);
// Information Response payload
auto expected_rsp = CreateStaticByteBuffer(
// Type = Connectionless MTU
0x01, 0x00,
// Result = Success
0x00, 0x00,
// Data (MTU)
0xdc, 0x02);
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspExtendedFeatures) {
BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
EXPECT_EQ(InformationType::kExtendedFeaturesSupported, type);
responder->SendExtendedFeaturesSupported(0xfaceb00c);
};
cmd_handler()->ServeInformationRequest(std::move(cb));
// Information Request payload
auto info_req = CreateStaticByteBuffer(
// Type = Features Mask
0x02, 0x00);
// Information Response payload
auto expected_rsp = CreateStaticByteBuffer(
// Type = Features Mask
0x02, 0x00,
// Result = Success
0x00, 0x00,
// Data (Mask)
0x0c, 0xb0, 0xce, 0xfa);
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspFixedChannels) {
BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
EXPECT_EQ(InformationType::kFixedChannelsSupported, type);
responder->SendFixedChannelsSupported(0xcafef00d4badc0deUL);
};
cmd_handler()->ServeInformationRequest(std::move(cb));
// Information Request payload
auto info_req = CreateStaticByteBuffer(
// Type = Fixed Channels
0x03, 0x00);
// Configuration Response payload
auto expected_rsp = CreateStaticByteBuffer(
// Type = Fixed Channels
0x03, 0x00,
// Result = Success
0x00, 0x00,
// Data (Mask)
0xde, 0xc0, 0xad, 0x4b, 0x0d, 0xf0, 0xfe, 0xca);
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConfigReqEmptyRspOkEmpty) {
BrEdrCommandHandler::ConfigurationRequestCallback cb =
[](ChannelId local_cid, uint16_t flags, ChannelConfiguration config, auto responder) {
EXPECT_EQ(kLocalCId, local_cid);
EXPECT_EQ(0x6006, flags);
EXPECT_FALSE(config.mtu_option().has_value());
EXPECT_FALSE(config.retransmission_flow_control_option().has_value());
EXPECT_EQ(0u, config.unknown_options().size());
responder->Send(kRemoteCId, 0x0001, ConfigurationResult::kPending,
ChannelConfiguration::ConfigurationOptions());
};
cmd_handler()->ServeConfigurationRequest(std::move(cb));
// Configuration Request payload
auto config_req = CreateStaticByteBuffer(
// Destination Channel ID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Flags
0x06, 0x60);
// Configuration Response payload
auto expected_rsp = CreateStaticByteBuffer(
// Destination Channel ID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags
0x01, 0x00,
// Result = Pending
0x04, 0x00);
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConfigurationRequest, config_req, expected_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConfigReqRspPendingEmpty) {
// Configuration Request payload
auto expected_config_req = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags (non-zero to test encoding)
0x01, 0x00,
// Data (Config Options)
0x01, // Type = MTU
0x02, // Length = 2
0x30, 0x00 // MTU = 48
);
// Configuration Response payload
auto pending_config_req = CreateStaticByteBuffer(
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Flags (non-zero to test encoding)
0x04, 0x00,
// Result = Pending
0x04, 0x00,
// Data (Config Options)
0x01, // Type = MTU
0x02, // Length = 2
0x60, 0x00 // MTU = 96
);
EXPECT_OUTBOUND_REQ(*fake_sig(), kConfigurationRequest, expected_config_req.view(),
{SignalingChannel::Status::kSuccess, pending_config_req.view()});
bool cb_called = false;
BrEdrCommandHandler::ConfigurationResponseCallback on_config_rsp =
[&cb_called](const BrEdrCommandHandler::ConfigurationResponse& rsp) {
cb_called = true;
EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
EXPECT_EQ(kLocalCId, rsp.local_cid());
EXPECT_EQ(0x0004, rsp.flags());
EXPECT_EQ(ConfigurationResult::kPending, rsp.result());
EXPECT_TRUE(rsp.config().mtu_option().has_value());
EXPECT_EQ(96u, rsp.config().mtu_option()->mtu());
return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
};
ChannelConfiguration config;
config.set_mtu_option(ChannelConfiguration::MtuOption(48));
EXPECT_TRUE(cmd_handler()->SendConfigurationRequest(kRemoteCId, 0x0001, config.Options(),
std::move(on_config_rsp)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConfigReqRspTimeOut) {
// Configuration Request payload
auto expected_config_req = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags (non-zero to test encoding)
0x01, 0xf0,
// Data (Config Options)
0x01, // Type = MTU
0x02, // Length = 2
0x30, 0x00 // MTU = 48
);
// Disconnect Request payload
auto expected_discon_req = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
EXPECT_OUTBOUND_REQ(*fake_sig(), kConfigurationRequest, expected_config_req.view(),
{SignalingChannel::Status::kTimeOut, {}});
EXPECT_OUTBOUND_REQ(*fake_sig(), kDisconnectionRequest, expected_discon_req.view());
set_request_fail_callback([this]() {
// Should still be allowed to send requests even after one failed
auto on_discon_rsp = [](auto&) {};
EXPECT_TRUE(
cmd_handler()->SendDisconnectionRequest(kRemoteCId, kLocalCId, std::move(on_discon_rsp)));
});
ASSERT_EQ(0u, failed_requests());
auto on_config_rsp = [](auto&) {
ADD_FAILURE();
return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
};
ChannelConfiguration config;
config.set_mtu_option(ChannelConfiguration::MtuOption(48));
EXPECT_TRUE(cmd_handler()->SendConfigurationRequest(kRemoteCId, 0xf001, config.Options(),
std::move(on_config_rsp)));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1u, failed_requests());
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspOk) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
// Information Response payload
auto ok_info_rsp = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
// Information Result
LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
// Data (extended features mask, 4 bytes)
LowerBits(kExtendedFeaturesBitFixedChannels), 0, 0, 0);
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, ok_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
cb_called = true;
EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
EXPECT_EQ(InformationResult::kSuccess, rsp.result());
EXPECT_EQ(InformationType::kExtendedFeaturesSupported, rsp.type());
EXPECT_EQ(kExtendedFeaturesBitFixedChannels, rsp.extended_features());
};
EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspNotSupported) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)));
// Information Response payload
auto error_info_rsp = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
// Information Result
LowerBits(static_cast<uint16_t>(InformationResult::kNotSupported)),
UpperBits(static_cast<uint16_t>(InformationResult::kNotSupported)));
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, error_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
cb_called = true;
EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
EXPECT_EQ(InformationResult::kNotSupported, rsp.result());
EXPECT_EQ(InformationType::kConnectionlessMTU, rsp.type());
};
EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kConnectionlessMTU,
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspHeaderNotEnoughBytes) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
// Information Response payload
auto malformed_info_rsp = CreateStaticByteBuffer(
// 1 of 4 bytes expected of an Information Response just to be able to parse it.
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, malformed_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) { cb_called = true; };
EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_FALSE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspPayloadNotEnoughBytes) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
// Information Response payload
auto malformed_info_rsp = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
// Information Result
LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
// Data (2 of 4 bytes expected of an extended features mask)
0, 0);
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, malformed_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) { cb_called = true; };
EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_FALSE(cb_called);
}
// Accept and pass a valid Information Response even if it doesn't have the type
// requested.
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspWrongType) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
// Information Response payload
auto mismatch_info_rsp = CreateStaticByteBuffer(
// Information Type
LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
// Information Result
LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
// Data (connectionless broadcast MTU, 2 bytes)
0x40, 0);
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, mismatch_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
cb_called = true;
EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
EXPECT_EQ(InformationResult::kSuccess, rsp.result());
EXPECT_EQ(InformationType::kConnectionlessMTU, rsp.type());
EXPECT_EQ(0x40, rsp.connectionless_mtu());
};
EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
// Allow types of information besides those known to BrEdrCommandHandler.
TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqUnknownType) {
// Information Request payload
auto expected_info_req = CreateStaticByteBuffer(
// Information Type
0x04, 0);
// Information Response payload
auto ok_info_rsp = CreateStaticByteBuffer(
// Information Type
0x04, 0,
// Information Result
LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
// Data (some payload)
't', 'e', 's', 't');
EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
{SignalingChannel::Status::kSuccess, ok_info_rsp.view()});
bool cb_called = false;
BrEdrCommandHandler::InformationResponseCallback on_info_cb =
[&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
cb_called = true;
EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
EXPECT_EQ(InformationResult::kSuccess, rsp.result());
EXPECT_EQ(static_cast<InformationType>(0x04), rsp.type());
};
EXPECT_TRUE(cmd_handler()->SendInformationRequest(static_cast<InformationType>(0x04),
std::move(on_info_cb)));
RunLoopUntilIdle();
EXPECT_TRUE(cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqRspPending) {
BrEdrCommandHandler::ConnectionRequestCallback cb = [](PSM psm, ChannelId remote_cid,
auto responder) {
EXPECT_EQ(kPsm, psm);
EXPECT_EQ(kRemoteCId, remote_cid);
responder->Send(kLocalCId, ConnectionResult::kPending, ConnectionStatus::kAuthorizationPending);
};
cmd_handler()->ServeConnectionRequest(std::move(cb));
// Connection Request payload
auto conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID (relative to requester)
LowerBits(kRemoteCId), UpperBits(kRemoteCId));
// Connection Response payload
auto conn_rsp = CreateStaticByteBuffer(
// Destination CID (relative to requester)
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Source CID (relative to requester)
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Connection Result
LowerBits(static_cast<uint16_t>(ConnectionResult::kPending)),
UpperBits(static_cast<uint16_t>(ConnectionResult::kPending)),
// Connection Status
LowerBits(static_cast<uint16_t>(ConnectionStatus::kAuthorizationPending)),
UpperBits(static_cast<uint16_t>(ConnectionStatus::kAuthorizationPending)));
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqBadPsm) {
constexpr uint16_t kBadPsm = 0x0002;
// Request callback shouldn't even be called for an invalid PSM.
bool req_cb_called = false;
BrEdrCommandHandler::ConnectionRequestCallback cb =
[&req_cb_called](PSM psm, ChannelId remote_cid, auto responder) { req_cb_called = true; };
cmd_handler()->ServeConnectionRequest(std::move(cb));
// Connection Request payload
auto conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kBadPsm), UpperBits(kBadPsm),
// Source CID (relative to requester)
LowerBits(kRemoteCId), UpperBits(kRemoteCId));
// Connection Response payload
auto conn_rsp = CreateStaticByteBuffer(
// Destination CID (relative to requester)
LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
// Source CID (relative to requester)
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Connection Result
LowerBits(static_cast<uint16_t>(ConnectionResult::kPSMNotSupported)),
UpperBits(static_cast<uint16_t>(ConnectionResult::kPSMNotSupported)),
// Connection Status
LowerBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)),
UpperBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)));
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
EXPECT_FALSE(req_cb_called);
}
TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqNonDynamicSrcCId) {
// Request callback shouldn't even be called for an invalid Source Channel ID.
bool req_cb_called = false;
BrEdrCommandHandler::ConnectionRequestCallback cb =
[&req_cb_called](PSM psm, ChannelId remote_cid, auto responder) { req_cb_called = true; };
cmd_handler()->ServeConnectionRequest(std::move(cb));
// Connection Request payload
auto conn_req = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID: fixed channel for Security Manager (relative to requester)
LowerBits(kSMPChannelId), UpperBits(kSMPChannelId));
// Connection Response payload
auto conn_rsp = CreateStaticByteBuffer(
// Destination CID (relative to requester)
LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
// Source CID (relative to requester)
LowerBits(kSMPChannelId), UpperBits(kSMPChannelId),
// Connection Result
LowerBits(static_cast<uint16_t>(ConnectionResult::kInvalidSourceCID)),
UpperBits(static_cast<uint16_t>(ConnectionResult::kInvalidSourceCID)),
// Connection Status
LowerBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)),
UpperBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)));
RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
EXPECT_FALSE(req_cb_called);
}
} // namespace
} // namespace bt::l2cap::internal