blob: 298224f819dc4d87757aed76f9be2c661fd7897b [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_dynamic_channel.h"
#include <lib/async/cpp/task.h>
#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/fake_signaling_channel.h"
namespace bt {
namespace l2cap {
namespace internal {
namespace {
// TODO(NET-1093): Add integration test with FakeChannelTest and
// BrEdrSignalingChannel using snooped connection data to verify signaling
// channel traffic.
constexpr uint16_t kPsm = 0x0001;
constexpr uint16_t kInvalidPsm = 0x0002; // Valid PSMs are odd.
constexpr ChannelId kLocalCId = 0x0040;
constexpr ChannelId kRemoteCId = 0x60a3;
constexpr ChannelId kBadCId = 0x003f; // Not a dynamic channel.
// Commands Reject
const ByteBuffer& kRejNotUnderstood = CreateStaticByteBuffer(
// Reject Reason (Not Understood)
0x00, 0x00);
// Connection Requests
const ByteBuffer& kConnReq = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
const ByteBuffer& kInboundConnReq = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId));
const ByteBuffer& kInboundInvalidPsmConnReq = CreateStaticByteBuffer(
// PSM
LowerBits(kInvalidPsm), UpperBits(kInvalidPsm),
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId));
const ByteBuffer& kInboundBadCIdConnReq = CreateStaticByteBuffer(
// PSM
LowerBits(kPsm), UpperBits(kPsm),
// Source CID
LowerBits(kBadCId), UpperBits(kBadCId));
// Connection Responses
const ByteBuffer& kPendingConnRsp = CreateStaticByteBuffer(
// Destination CID
0x00, 0x00,
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Pending)
0x01, 0x00,
// Status (Authorization Pending)
0x02, 0x00);
const ByteBuffer& kPendingConnRspWithId = CreateStaticByteBuffer(
// Destination CID (Wrong endianness but valid)
UpperBits(kRemoteCId), LowerBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Pending)
0x01, 0x00,
// Status (Authorization Pending)
0x02, 0x00);
const ByteBuffer& kOkConnRsp = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Successful)
0x00, 0x00,
// Status (No further information available)
0x00, 0x00);
const ByteBuffer& kInvalidConnRsp = CreateStaticByteBuffer(
// Destination CID (Not a dynamic channel ID)
LowerBits(kBadCId), UpperBits(kBadCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (Successful)
0x00, 0x00,
// Status (No further information available)
0x00, 0x00);
const ByteBuffer& kRejectConnRsp = CreateStaticByteBuffer(
// Destination CID (Invalid)
LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Result (No resources)
0x04, 0x00,
// Status (No further information available)
0x00, 0x00);
const ByteBuffer& kInboundOkConnRsp = CreateStaticByteBuffer(
// Destination CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Result (Successful)
0x00, 0x00,
// Status (No further information available)
0x00, 0x00);
const ByteBuffer& kInboundBadPsmConnRsp = CreateStaticByteBuffer(
// Destination CID (Invalid)
0x00, 0x00,
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Result (PSM Not Supported)
0x02, 0x00,
// Status (No further information available)
0x00, 0x00);
const ByteBuffer& kInboundBadCIdConnRsp = CreateStaticByteBuffer(
// Destination CID (Invalid)
0x00, 0x00,
// Source CID
LowerBits(kBadCId), UpperBits(kBadCId),
// Result (Invalid Source CID)
0x06, 0x00,
// Status (No further information available)
0x00, 0x00);
// Disconnection Requests
const ByteBuffer& kDisconReq = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId));
const ByteBuffer& kInboundDisconReq = CreateStaticByteBuffer(
// Destination CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId));
// Disconnection Responses
const ByteBuffer& kInboundDisconRsp = kInboundDisconReq;
const ByteBuffer& kDisconRsp = kDisconReq;
// Configuration Requests
const ByteBuffer& kConfigReq = CreateStaticByteBuffer(
// Destination CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags
0x00, 0x00);
const ByteBuffer& kInboundConfigReq = CreateStaticByteBuffer(
// Destination CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Flags
0x00, 0x00);
// Configuration Responses
const ByteBuffer& kOkConfigRsp = CreateStaticByteBuffer(
// Source CID
LowerBits(kLocalCId), UpperBits(kLocalCId),
// Flags
0x00, 0x00,
// Result (Successful)
0x00, 0x00);
const ByteBuffer& kUnknownIdConfigRsp = CreateStaticByteBuffer(
// Source CID (Invalid)
LowerBits(kBadCId), UpperBits(kBadCId),
// Flags
0x00, 0x00,
// Result (Successful)
0x00, 0x00);
const ByteBuffer& kPendingConfigRsp = CreateStaticByteBuffer(
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags
0x00, 0x00,
// Result (Pending)
0x04, 0x00);
const ByteBuffer& kInboundOkConfigRsp = CreateStaticByteBuffer(
// Source CID
LowerBits(kRemoteCId), UpperBits(kRemoteCId),
// Flags
0x00, 0x00,
// Result (Successful)
0x00, 0x00);
class L2CAP_BrEdrDynamicChannelTest : public ::gtest::TestLoopFixture {
public:
L2CAP_BrEdrDynamicChannelTest() = default;
~L2CAP_BrEdrDynamicChannelTest() override = default;
protected:
// Import types for brevity.
using DynamicChannelCallback = DynamicChannelRegistry::DynamicChannelCallback;
using ServiceRequestCallback = DynamicChannelRegistry::ServiceRequestCallback;
// TestLoopFixture overrides
void SetUp() override {
TestLoopFixture::SetUp();
channel_close_cb_ = nullptr;
service_request_cb_ = nullptr;
signaling_channel_ =
std::make_unique<testing::FakeSignalingChannel>(dispatcher());
registry_ = std::make_unique<BrEdrDynamicChannelRegistry>(
sig(),
fit::bind_member(this, &L2CAP_BrEdrDynamicChannelTest::OnChannelClose),
fit::bind_member(this,
&L2CAP_BrEdrDynamicChannelTest::OnServiceRequest));
}
void TearDown() override {
RunLoopUntilIdle();
registry_ = nullptr;
signaling_channel_ = nullptr;
service_request_cb_ = nullptr;
channel_close_cb_ = nullptr;
TestLoopFixture::TearDown();
}
testing::FakeSignalingChannel* sig() const {
return signaling_channel_.get();
}
BrEdrDynamicChannelRegistry* registry() const { return registry_.get(); }
void set_channel_close_cb(DynamicChannelCallback close_cb) {
channel_close_cb_ = std::move(close_cb);
}
void set_service_request_cb(ServiceRequestCallback service_request_cb) {
service_request_cb_ = std::move(service_request_cb);
}
private:
void OnChannelClose(const DynamicChannel* channel) {
if (channel_close_cb_) {
channel_close_cb_(channel);
}
}
// Default to rejecting all service requests if no test callback is set.
DynamicChannelCallback OnServiceRequest(PSM psm) {
if (service_request_cb_) {
return service_request_cb_(psm);
}
return nullptr;
}
DynamicChannelCallback channel_close_cb_;
ServiceRequestCallback service_request_cb_;
std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
std::unique_ptr<BrEdrDynamicChannelRegistry> registry_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_BrEdrDynamicChannelTest);
};
TEST_F(L2CAP_BrEdrDynamicChannelTest, FailConnectChannel) {
EXPECT_OUTBOUND_REQ(
*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kRejectConnRsp.view()});
// Build channel and operate it directly to be able to inspect it in the
// connected but not open state.
auto channel =
BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId);
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
EXPECT_EQ(kLocalCId, channel->local_cid());
int open_result_cb_count = 0;
auto open_result_cb = [&open_result_cb_count, &channel] {
if (open_result_cb_count == 0) {
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
}
open_result_cb_count++;
};
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
channel->Open(std::move(open_result_cb));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1, open_result_cb_count);
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
EXPECT_EQ(kInvalidChannelId, channel->remote_cid());
// A failed-to-open channel should not invoke the close callback.
EXPECT_EQ(0, close_cb_count);
// No disconnection transaction expected because the channel isn't actually
// owned by the registry.
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, ConnectChannelFailConfig) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kReject, kRejNotUnderstood.view()});
// Build channel and operate it directly to be able to inspect it in the
// connected but not open state.
auto channel =
BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId);
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
EXPECT_EQ(kLocalCId, channel->local_cid());
int open_result_cb_count = 0;
auto open_result_cb = [&open_result_cb_count, &channel] {
if (open_result_cb_count == 0) {
EXPECT_TRUE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
}
open_result_cb_count++;
};
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
channel->Open(std::move(open_result_cb));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_TRUE(channel->IsConnected());
// A connected channel should have a valid remote channel ID.
EXPECT_EQ(kRemoteCId, channel->remote_cid());
EXPECT_FALSE(channel->IsOpen());
EXPECT_EQ(1, open_result_cb_count);
// A failed-to-open channel should not invoke the close callback.
EXPECT_EQ(0, close_cb_count);
// No disconnection transaction expected because the channel isn't actually
// owned by the registry.
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, ConnectChannelFailInvalidResponse) {
EXPECT_OUTBOUND_REQ(
*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kInvalidConnRsp.view()});
// Build channel and operate it directly to be able to inspect it in the
// connected but not open state.
auto channel =
BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId);
int open_result_cb_count = 0;
auto open_result_cb = [&open_result_cb_count, &channel] {
if (open_result_cb_count == 0) {
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
}
open_result_cb_count++;
};
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
channel->Open(std::move(open_result_cb));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_FALSE(channel->IsConnected());
EXPECT_FALSE(channel->IsOpen());
EXPECT_EQ(1, open_result_cb_count);
EXPECT_EQ(0, close_cb_count);
// No disconnection transaction expected because the channel isn't actually
// owned by the registry.
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenAndLocalCloseChannel) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
auto open_cb = [&open_cb_count](auto chan) {
if (open_cb_count == 0) {
ASSERT_TRUE(chan);
EXPECT_TRUE(chan->IsOpen());
EXPECT_TRUE(chan->IsConnected());
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
}
open_cb_count++;
};
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto chan) {
EXPECT_TRUE(chan);
close_cb_count++;
});
registry()->OpenOutbound(kPsm, std::move(open_cb));
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, open_cb_count);
EXPECT_EQ(0, close_cb_count);
registry()->CloseChannel(kLocalCId);
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1, open_cb_count);
// Local channel closure shouldn't trigger the close callback.
EXPECT_EQ(0, close_cb_count);
// Repeated closure of the same channel should not have any effect.
registry()->CloseChannel(kLocalCId);
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1, open_cb_count);
EXPECT_EQ(0, close_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenAndRemoteCloseChannel) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
int open_cb_count = 0;
auto open_cb = [&open_cb_count](auto chan) { open_cb_count++; };
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto chan) {
ASSERT_TRUE(chan);
EXPECT_FALSE(chan->IsOpen());
EXPECT_FALSE(chan->IsConnected());
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
close_cb_count++;
});
registry()->OpenOutbound(kPsm, std::move(open_cb));
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, open_cb_count);
EXPECT_EQ(0, close_cb_count);
RETURN_IF_FATAL(sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq,
kInboundDisconRsp));
EXPECT_EQ(1, open_cb_count);
// Remote channel closure should trigger the close callback.
EXPECT_EQ(1, close_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelWithPendingConn) {
EXPECT_OUTBOUND_REQ(
*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kPendingConnRsp.view()},
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
registry()->OpenOutbound(kPsm, [&open_cb_count](auto chan) {
open_cb_count++;
ASSERT_TRUE(chan);
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
});
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelMismatchConnRsp) {
// The first Connection Response (pending) has a different ID than the final
// Connection Response (success).
EXPECT_OUTBOUND_REQ(
*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kPendingConnRspWithId.view()},
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
registry()->OpenOutbound(kPsm, [&open_cb_count](auto chan) {
open_cb_count++;
ASSERT_TRUE(chan);
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
});
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelConfigPending) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kPendingConfigRsp.view()},
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
registry()->OpenOutbound(kPsm, [&open_cb_count](auto chan) {
open_cb_count++;
ASSERT_TRUE(chan);
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
});
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest,
OpenChannelRemoteDisconnectWhileConfiguring) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
auto config_id =
EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kConfigReq.view());
int open_cb_count = 0;
registry()->OpenOutbound(kPsm, [&open_cb_count](auto chan) {
open_cb_count++;
EXPECT_FALSE(chan);
});
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq,
kInboundDisconRsp));
// Response handler should return false ("no more responses") when called, so
// trigger single responses rather than a set of two.
RETURN_IF_FATAL(sig()->ReceiveResponses(
config_id,
{{SignalingChannel::Status::kSuccess, kPendingConfigRsp.view()}}));
RETURN_IF_FATAL(sig()->ReceiveResponses(
config_id, {{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()}}));
EXPECT_EQ(1, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelConfigWrongId) {
EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
{SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kUnknownIdConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
registry()->OpenOutbound(kPsm, [&open_cb_count](auto chan) {
open_cb_count++;
EXPECT_FALSE(chan);
});
RETURN_IF_FATAL(RunLoopUntilIdle());
RETURN_IF_FATAL(sig()->ReceiveExpectRejectInvalidChannelId(
kConfigurationRequest, kInboundConfigReq, kLocalCId, kInvalidChannelId));
EXPECT_EQ(1, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionOk) {
EXPECT_OUTBOUND_REQ(
*sig(), kConfigurationRequest, kConfigReq.view(),
{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()});
EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
{SignalingChannel::Status::kSuccess, kDisconRsp.view()});
int open_cb_count = 0;
DynamicChannelCallback open_cb = [&open_cb_count](auto chan) {
open_cb_count++;
ASSERT_TRUE(chan);
EXPECT_EQ(kPsm, chan->psm());
EXPECT_EQ(kLocalCId, chan->local_cid());
EXPECT_EQ(kRemoteCId, chan->remote_cid());
};
int service_request_cb_count = 0;
ServiceRequestCallback service_request_cb =
[&service_request_cb_count, open_cb = std::move(open_cb)](
PSM psm) mutable -> DynamicChannelCallback {
service_request_cb_count++;
EXPECT_EQ(kPsm, psm);
if (psm == kPsm) {
return open_cb.share();
}
return nullptr;
};
set_service_request_cb(std::move(service_request_cb));
int close_cb_count = 0;
set_channel_close_cb([&close_cb_count](auto chan) { close_cb_count++; });
RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq,
kInboundOkConnRsp));
RETURN_IF_FATAL(RunLoopUntilIdle());
EXPECT_EQ(1, service_request_cb_count);
EXPECT_EQ(0, open_cb_count);
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
EXPECT_EQ(1, service_request_cb_count);
EXPECT_EQ(1, open_cb_count);
registry()->CloseChannel(kLocalCId);
EXPECT_EQ(0, close_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest,
InboundConnectionRemoteDisconnectWhileConfiguring) {
auto config_id =
EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kConfigReq.view());
int open_cb_count = 0;
DynamicChannelCallback open_cb = [&open_cb_count](auto chan) {
open_cb_count++;
FAIL() << "Failed-to-open inbound channels shouldn't trip open callback";
};
int service_request_cb_count = 0;
ServiceRequestCallback service_request_cb =
[&service_request_cb_count, open_cb = std::move(open_cb)](
PSM psm) mutable -> DynamicChannelCallback {
service_request_cb_count++;
EXPECT_EQ(kPsm, psm);
if (psm == kPsm) {
return open_cb.share();
}
return nullptr;
};
set_service_request_cb(std::move(service_request_cb));
RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq,
kInboundOkConnRsp));
RunLoopUntilIdle();
EXPECT_EQ(1, service_request_cb_count);
EXPECT_EQ(0, open_cb_count);
RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq,
kInboundOkConfigRsp));
RETURN_IF_FATAL(sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq,
kInboundDisconRsp));
// Drop response received after the channel is disconnected.
RETURN_IF_FATAL(sig()->ReceiveResponses(
config_id, {{SignalingChannel::Status::kSuccess, kOkConfigRsp.view()}}));
EXPECT_EQ(1, service_request_cb_count);
// Channel that failed to open shouldn't have triggered channel open callback.
EXPECT_EQ(0, open_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionInvalidPsm) {
ServiceRequestCallback service_request_cb =
[](PSM psm) -> DynamicChannelCallback {
// Write user code that accepts the invalid PSM, but control flow may not
// reach here.
EXPECT_EQ(kInvalidPsm, psm);
if (psm == kInvalidPsm) {
return [](auto) { FAIL() << "Channel should fail to open for PSM"; };
}
return nullptr;
};
set_service_request_cb(std::move(service_request_cb));
RETURN_IF_FATAL(sig()->ReceiveExpect(
kConnectionRequest, kInboundInvalidPsmConnReq, kInboundBadPsmConnRsp));
RunLoopUntilIdle();
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionUnsupportedPsm) {
int service_request_cb_count = 0;
ServiceRequestCallback service_request_cb =
[&service_request_cb_count](PSM psm) -> DynamicChannelCallback {
service_request_cb_count++;
EXPECT_EQ(kPsm, psm);
// Reject the service request.
return nullptr;
};
set_service_request_cb(std::move(service_request_cb));
RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq,
kInboundBadPsmConnRsp));
RunLoopUntilIdle();
EXPECT_EQ(1, service_request_cb_count);
}
TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionInvalidSrcCId) {
ServiceRequestCallback service_request_cb =
[](PSM psm) -> DynamicChannelCallback {
// Control flow may not reach here.
EXPECT_EQ(kPsm, psm);
if (psm == kPsm) {
return [](auto) { FAIL() << "Channel from src_cid should fail to open"; };
}
return nullptr;
};
set_service_request_cb(std::move(service_request_cb));
RETURN_IF_FATAL(sig()->ReceiveExpect(
kConnectionRequest, kInboundBadCIdConnReq, kInboundBadCIdConnRsp));
RunLoopUntilIdle();
}
} // namespace
} // namespace internal
} // namespace l2cap
} // namespace bt