blob: 6c9f3b1a8296c60ab201572a399b9d5a0374ebcb [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 "garnet/drivers/bluetooth/lib/data/domain.h"
#include <fbl/macros.h>
#include <lib/async/cpp/task.h>
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "garnet/drivers/bluetooth/lib/common/test_helpers.h"
#include "garnet/drivers/bluetooth/lib/testing/fake_controller_test.h"
#include "garnet/drivers/bluetooth/lib/testing/test_controller.h"
// This test harness provides test cases for interations between L2CAP, RFCOMM,
// and SocketFactory in integration, as they are implemented by the domain
// object. These exercise a production data plane against raw HCI endpoints.
namespace btlib {
namespace data {
namespace {
using ::btlib::testing::TestController;
using TestingBase = ::btlib::testing::FakeControllerTest<TestController>;
using common::CreateStaticByteBuffer;
using common::LowerBits;
using common::StaticByteBuffer;
using common::UpperBits;
class DATA_DomainTest : public TestingBase {
public:
DATA_DomainTest() = default;
~DATA_DomainTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
InitializeACLDataChannel();
domain_ = Domain::CreateWithDispatcher(transport(), dispatcher());
domain_->Initialize();
StartTestDevice();
}
void TearDown() override {
domain_->ShutDown();
domain_ = nullptr;
TestingBase::TearDown();
}
// TODO(armansito): Move this to the testing library. This should set up
// expectations on the TestController and not just transmit.
void EmulateIncomingChannelCreation(hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id,
l2cap::ChannelId dst_id, l2cap::PSM psm) {
// clang-format off
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 12 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Connection Request (ID: 1, length: 4, |psm|, |src_id|)
0x02, 0x01, 0x04, 0x00,
LowerBits(psm), UpperBits(psm), LowerBits(src_id), UpperBits(src_id)));
RunLoopUntilIdle();
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 16 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, |dst_id|, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
0x04, 0x06, 0x08, 0x00,
LowerBits(dst_id), UpperBits(dst_id), 0x00, 0x00,
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 14 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0e, 0x00,
// L2CAP B-frame header (length: 10 bytes, channel-id: 0x0001 (ACL sig))
0x0a, 0x00, 0x01, 0x00,
// Configuration Response (ID: 1, length: 6, src cid: |dst_id|, flags: 0,
// result: success)
0x05, 0x01, 0x06, 0x00,
LowerBits(dst_id), UpperBits(dst_id), 0x00, 0x00,
0x00, 0x00));
// clang-format on
RunLoopUntilIdle();
}
void ExpectOutgoingChannelCreation(hci::ConnectionHandle link_handle,
l2cap::ChannelId remote_id,
l2cap::ChannelId expected_local_cid,
l2cap::PSM psm) {
auto response_cb = [=](const auto& bytes) {
ASSERT_LE(10u, bytes.size());
EXPECT_EQ(LowerBits(link_handle), bytes[0]);
EXPECT_EQ(UpperBits(link_handle), bytes[1]);
l2cap::CommandCode code = bytes[8];
auto id = bytes[9];
switch (code) {
case l2cap::kConnectionRequest: {
ASSERT_EQ(16u, bytes.size());
EXPECT_EQ(LowerBits(psm), bytes[12]);
EXPECT_EQ(UpperBits(psm), bytes[13]);
EXPECT_EQ(LowerBits(expected_local_cid), bytes[14]);
EXPECT_EQ(UpperBits(expected_local_cid), bytes[15]);
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: |link handle|, length: 16 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x10, 0x00,
// L2CAP B-frame header: length 12, channel-id 1 (signaling)
0x0c, 0x00, 0x01, 0x00,
// Connection Response (0x03) id: (matches request), length 8
l2cap::kConnectionResponse, id, 0x08, 0x00,
// destination cid |remote_id|
LowerBits(remote_id), UpperBits(remote_id),
// source cid (same as request)
bytes[14], bytes[15],
// Result (success), status
0x00, 0x00, 0x00, 0x00));
return;
}
case l2cap::kConfigurationRequest: {
ASSERT_LE(16u, bytes.size());
// Just blindly accept any configuration request as long as it for our
// cid
EXPECT_EQ(LowerBits(remote_id), bytes[12]);
EXPECT_EQ(UpperBits(remote_id), bytes[13]);
// Respond to the given request, and make your own request.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL header: handle |link_handle|, length: 18
LowerBits(link_handle), UpperBits(link_handle), 0x12, 0x00,
// L2CAP B-frame header: length 14, channel-id 1 (signaling)
0x0e, 0x00, 0x01, 0x00,
// Configuration Response (0x04), id: (match request) length 10
l2cap::kConfigurationResponse, id, 0x0a, 0x00,
// Source CID, Flags (same as req)
LowerBits(expected_local_cid), UpperBits(expected_local_cid),
bytes[14], bytes[15],
// Result (success)
0x00, 0x00,
// Config option: MTU, length 2, MTU 1024
0x01, 0x02, 0x00, 0x04));
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 16 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL
// sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID: 6, length: 8, |dst_id|, flags: 0,
// options: [type: MTU, length: 2, MTU: 1024])
l2cap::kConfigurationRequest, 0x06, 0x08, 0x00,
LowerBits(expected_local_cid), UpperBits(expected_local_cid),
0x00, 0x00, 0x01, 0x02, 0x00, 0x04));
return;
}
case l2cap::kConfigurationResponse: {
ASSERT_LE(16u, bytes.size());
// Should indicate our cid
EXPECT_EQ(LowerBits(remote_id), bytes[12]);
EXPECT_EQ(UpperBits(remote_id), bytes[13]);
return;
}
default:
ASSERT_TRUE(false);
return;
};
};
test_device()->SetDataCallback(response_cb, dispatcher());
}
Domain* domain() const { return domain_.get(); }
private:
fbl::RefPtr<Domain> domain_;
DISALLOW_COPY_ASSIGN_AND_MOVE(DATA_DomainTest);
};
TEST_F(DATA_DomainTest, InboundL2capSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVDTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
// Register a fake link.
domain()->AddACLConnection(
kLinkHandle, hci::Connection::Role::kMaster, [] {},
[](auto, auto, auto) {}, dispatcher());
zx::socket sock;
ASSERT_FALSE(sock);
auto sock_cb = [&](zx::socket cb_sock, hci::ConnectionHandle handle) {
EXPECT_EQ(kLinkHandle, handle);
sock = std::move(cb_sock);
};
domain()->RegisterService(kPSM, std::move(sock_cb), dispatcher());
RunLoopUntilIdle();
EmulateIncomingChannelCreation(kLinkHandle, kRemoteId, kLocalId, kPSM);
ASSERT_TRUE(sock);
// Test basic channel<->socket interaction by verifying that an ACL packet
// gets routed to the socket.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00, 't', 'e', 's', 't'));
// Run until the packet is written to the socket buffer.
RunLoopUntilIdle();
// Allocate a larger buffer than the number of SDU bytes we expect (which is
// 4).
StaticByteBuffer<10> socket_bytes;
size_t bytes_read;
zx_status_t status = sock.read(0, socket_bytes.mutable_data(),
socket_bytes.size(), &bytes_read);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(4u, bytes_read);
EXPECT_EQ("test", socket_bytes.view(0, bytes_read).AsString());
}
TEST_F(DATA_DomainTest, OutboundL2apSocket) {
constexpr l2cap::PSM kPSM = l2cap::kAVCTP;
constexpr l2cap::ChannelId kLocalId = 0x0040;
constexpr l2cap::ChannelId kRemoteId = 0x9042;
constexpr hci::ConnectionHandle kLinkHandle = 0x0001;
// Register a fake link.
domain()->AddACLConnection(
kLinkHandle, hci::Connection::Role::kMaster, [] {},
[](auto, auto, auto) {}, dispatcher());
zx::socket sock;
ASSERT_FALSE(sock);
auto sock_cb = [&](zx::socket cb_sock, hci::ConnectionHandle handle) {
EXPECT_EQ(kLinkHandle, handle);
sock = std::move(cb_sock);
};
domain()->OpenL2capChannel(kLinkHandle, kPSM, std::move(sock_cb),
dispatcher());
// Expect the CHANNEL opening stuff here
ExpectOutgoingChannelCreation(kLinkHandle, kRemoteId, kLocalId, kPSM);
RunLoopUntilIdle();
// We should have opened a channel successfully.
ASSERT_TRUE(sock);
// Test basic channel<->socket interaction by verifying that an ACL packet
// gets routed to the socket.
test_device()->SendACLDataChannelPacket(CreateStaticByteBuffer(
// ACL data header (handle: 1, length 8)
0x01, 0x00, 0x08, 0x00,
// L2CAP B-frame: (length: 4, channel-id: 0x0040 (kLocalId))
0x04, 0x00, 0x40, 0x00, 't', 'e', 's', 't'));
// Run until the packet is written to the socket buffer.
RunLoopUntilIdle();
// Allocate a larger buffer than the number of SDU bytes we expect (which is
// 4).
StaticByteBuffer<10> socket_bytes;
size_t bytes_read;
zx_status_t status = sock.read(0, socket_bytes.mutable_data(),
socket_bytes.size(), &bytes_read);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(4u, bytes_read);
EXPECT_EQ("test", socket_bytes.view(0, bytes_read).AsString());
}
// TODO(armansito): Add unit tests for RFCOMM sockets when the Domain class
// has a public API for it.
} // namespace
} // namespace data
} // namespace btlib