blob: c378508979b3154cd1bf66b1e34eae21ff3d42f1 [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/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/hci.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/fake_controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/test_controller.h"
namespace bt {
namespace hci {
namespace {
constexpr ConnectionHandle kTestHandle = 0x0001;
const LEConnectionParameters kTestParams(1, 1, 1);
const DeviceAddress kLEAddress1(DeviceAddress::Type::kLEPublic,
"00:00:00:00:00:01");
const DeviceAddress kLEAddress2(DeviceAddress::Type::kLEPublic,
"00:00:00:00:00:02");
const DeviceAddress kACLAddress1(DeviceAddress::Type::kBREDR,
"00:00:00:00:00:03");
const DeviceAddress kACLAddress2(DeviceAddress::Type::kBREDR,
"00:00:00:00:00:04");
constexpr UInt128 kLTK{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
constexpr uint64_t kRand = 1;
constexpr uint16_t kEDiv = 255;
const DataBufferInfo kBrEdrBufferInfo(1024, 5);
const DataBufferInfo kLeBufferInfo(1024, 1);
using bt::testing::CommandTransaction;
using TestingBase =
bt::testing::FakeControllerTest<bt::testing::TestController>;
class ConnectionTest : public TestingBase {
public:
ConnectionTest() = default;
~ConnectionTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
InitializeACLDataChannel(kBrEdrBufferInfo, kLeBufferInfo);
StartTestDevice();
}
ConnectionPtr NewLEConnection(
Connection::Role role = Connection::Role::kMaster) {
return Connection::CreateLE(kTestHandle, role, kLEAddress1, kLEAddress2,
kTestParams, transport());
}
ConnectionPtr NewACLConnection(
Connection::Role role = Connection::Role::kMaster) {
return Connection::CreateACL(kTestHandle, role, kACLAddress1, kACLAddress2,
transport());
}
};
// Tests using this harness will be instantiated using ACL and LE link types.
// See INSTANTIATE_TEST_SUITE_P(HCI_ConnectionTest, LinkTypeConnectionTest, ...)
class LinkTypeConnectionTest
: public ConnectionTest,
public ::testing::WithParamInterface<Connection::LinkType> {
protected:
ConnectionPtr NewConnection(
Connection::Role role = Connection::Role::kMaster) {
const Connection::LinkType ll_type = GetParam();
switch (ll_type) {
case Connection::LinkType::kACL:
return NewACLConnection(role);
case Connection::LinkType::kLE:
return NewLEConnection(role);
default:
break;
}
ZX_PANIC("Invalid link type: %u", static_cast<unsigned>(ll_type));
return nullptr;
}
};
using HCI_ConnectionTest = ConnectionTest;
TEST_F(HCI_ConnectionTest, Getters) {
auto connection = NewLEConnection();
EXPECT_EQ(Connection::LinkType::kLE, connection->ll_type());
EXPECT_EQ(kTestHandle, connection->handle());
EXPECT_EQ(Connection::Role::kMaster, connection->role());
EXPECT_EQ(kTestParams, connection->low_energy_parameters());
EXPECT_EQ(kLEAddress1, connection->local_address());
EXPECT_EQ(kLEAddress2, connection->peer_address());
EXPECT_TRUE(connection->is_open());
}
TEST_P(LinkTypeConnectionTest, Close) {
// clang-format off
// HCI_Disconnect (handle: 0x0001, reason: RemoteUserTerminatedConnection)
auto req_bytes = CreateStaticByteBuffer(
0x06, 0x04, 0x03, 0x01, 0x00, StatusCode::kRemoteUserTerminatedConnection);
// Respond with Command Status and Disconnection Complete.
auto cmd_status_bytes = CreateStaticByteBuffer(
kCommandStatusEventCode, 0x04, StatusCode::kSuccess, 1, 0x06, 0x04);
auto disc_cmpl_bytes = CreateStaticByteBuffer(
kDisconnectionCompleteEventCode, 0x04,
StatusCode::kSuccess, 0x01, 0x00, StatusCode::kConnectionTerminatedByLocalHost);
// clang-format on
test_device()->QueueCommandTransaction(req_bytes,
{&cmd_status_bytes, &disc_cmpl_bytes});
bool callback_called = false;
test_device()->SetTransactionCallback(
[&callback_called] { callback_called = true; }, dispatcher());
auto connection = NewConnection();
EXPECT_TRUE(connection->is_open());
connection->Close(StatusCode::kRemoteUserTerminatedConnection);
EXPECT_FALSE(connection->is_open());
RunLoopUntilIdle();
EXPECT_TRUE(callback_called);
}
TEST_P(LinkTypeConnectionTest, CloseError) {
// clang-format off
// HCI_Disconnect (handle: 0x0001, reason: RemoteUserTerminatedConnection)
auto req_bytes = CreateStaticByteBuffer(
0x06, 0x04, 0x03, 0x01, 0x00, StatusCode::kRemoteUserTerminatedConnection);
// Respond with Command Status and Disconnection Complete.
auto cmd_status_bytes = CreateStaticByteBuffer(
kCommandStatusEventCode, 0x04, StatusCode::kSuccess, 1, 0x06, 0x04);
auto disc_cmpl_bytes = CreateStaticByteBuffer(
kDisconnectionCompleteEventCode, 0x04,
StatusCode::kCommandDisallowed, 0x01, 0x00, StatusCode::kConnectionTerminatedByLocalHost);
// clang-format on
test_device()->QueueCommandTransaction(req_bytes,
{&cmd_status_bytes, &disc_cmpl_bytes});
// The callback should get called regardless of the procedure status.
bool callback_called = false;
test_device()->SetTransactionCallback(
[&callback_called] { callback_called = true; }, dispatcher());
auto connection = NewConnection();
EXPECT_TRUE(connection->is_open());
connection->Close(StatusCode::kRemoteUserTerminatedConnection);
EXPECT_FALSE(connection->is_open());
RunLoopUntilIdle();
EXPECT_TRUE(callback_called);
}
TEST_F(HCI_ConnectionTest, StartEncryptionFailsAsSlave) {
auto conn = NewLEConnection(Connection::Role::kSlave);
conn->set_link_key(LinkKey());
EXPECT_FALSE(conn->StartEncryption());
}
// TODO(BT-374): This test is temporary to document the current state of
// support for link layer encryption. Remove this and replace it with new tests
// when StartEncryption can work with BR/EDR.
TEST_F(HCI_ConnectionTest, StartEncryptionNotSupportedOnACL) {
auto conn = NewACLConnection();
conn->set_link_key(LinkKey());
EXPECT_FALSE(conn->StartEncryption());
}
TEST_P(LinkTypeConnectionTest, StartEncryptionNoLinkKey) {
auto conn = NewConnection();
EXPECT_FALSE(conn->StartEncryption());
}
// HCI Command Status event is received with an error status.
TEST_F(HCI_ConnectionTest, LEStartEncryptionFailsAtStatus) {
// clang-format off
auto kExpectedCommand = CreateStaticByteBuffer(
0x19, 0x20, // HCI_LE_Start_Encryption
28, // parameter total size
0x01, 0x00, // connection handle: 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rand: 1
0xFF, 0x00, // ediv: 255
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 // LTK
);
auto kErrorStatus = CreateStaticByteBuffer(
0x0F, // HCI Command Status event code
4, // parameter total size
0x0C, // "Command Disallowed" error
1, // num_hci_command_packets
0x19, 0x20 // opcode: HCI_LE_Start_Encryption
);
// clang-format on
test_device()->QueueCommandTransaction(kExpectedCommand, {&kErrorStatus});
bool callback = false;
auto conn = NewLEConnection();
conn->set_link_key(LinkKey(kLTK, kRand, kEDiv));
conn->set_encryption_change_callback([&](Status status, bool enabled) {
EXPECT_FALSE(status);
EXPECT_FALSE(enabled);
EXPECT_EQ(StatusCode::kCommandDisallowed, status.protocol_error());
callback = true;
});
EXPECT_TRUE(conn->StartEncryption());
RunLoopUntilIdle();
EXPECT_TRUE(callback);
}
TEST_F(HCI_ConnectionTest, LEStartEncryptionSuccess) {
auto kExpectedCommand = CreateStaticByteBuffer(
0x19, 0x20, // HCI_LE_Start_Encryption
28, // parameter total size
0x01, 0x00, // connection handle: 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rand: 1
0xFF, 0x00, // ediv: 255
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 // LTK
);
auto kStatus =
CreateStaticByteBuffer(0x0F, // HCI Command Status event code
4, // parameter total size
0x00, // success status
1, // num_hci_command_packets
0x19, 0x20 // opcode: HCI_LE_Start_Encryption
);
test_device()->QueueCommandTransaction(kExpectedCommand, {&kStatus});
bool callback = false;
auto conn = NewLEConnection();
conn->set_link_key(LinkKey(kLTK, kRand, kEDiv));
conn->set_encryption_change_callback(
[&](Status status, bool enabled) { callback = true; });
EXPECT_TRUE(conn->StartEncryption());
// Callback shouldn't be called until the controller sends an encryption
// changed event.
RunLoopUntilIdle();
EXPECT_FALSE(callback);
}
TEST_P(LinkTypeConnectionTest, EncryptionChangeIgnoredEvents) {
// clang-format off
auto kEncChangeMalformed = CreateStaticByteBuffer(
0x08, // HCI Encryption Change event code
3, // parameter total size
0x00, // status
0x01, 0x00 // connection handle: 1
// Last byte missing
);
auto kEncChangeWrongHandle = CreateStaticByteBuffer(
0x08, // HCI Encryption Change event code
4, // parameter total size
0x00, // status
0x02, 0x00, // connection handle: 2
0x01 // encryption enabled
);
// clang-format on
bool callback = false;
auto conn = NewConnection();
conn->set_link_key(LinkKey(kLTK, kRand, kEDiv));
conn->set_encryption_change_callback([&](Status, bool) { callback = true; });
test_device()->SendCommandChannelPacket(kEncChangeMalformed);
test_device()->SendCommandChannelPacket(kEncChangeWrongHandle);
RunLoopUntilIdle();
EXPECT_FALSE(callback);
}
const auto kEncryptionChangeEventEnabled =
CreateStaticByteBuffer(0x08, // HCI Encryption Change event code
4, // parameter total size
0x00, // status
0x01, 0x00, // connection handle: 1
0x01 // encryption enabled
);
const auto kReadEncryptionKeySizeCommand =
CreateStaticByteBuffer(0x08, 0x14, // opcode: HCI_ReadEncryptionKeySize
0x02, // parameter size
0x01, 0x00 // connection handle: 0x0001
);
const auto kDisconnectCommand =
CreateStaticByteBuffer(0x06, 0x04, // opcode: HCI_Disconnect
0x03, // parameter total size
0x01, 0x00, // handle: 1
0x05 // reason: authentication failure
);
TEST_P(LinkTypeConnectionTest, EncryptionChangeEvents) {
// clang-format off
auto kEncryptionChangeEventDisabled = CreateStaticByteBuffer(
0x08, // HCI Encryption Change event code
4, // parameter total size
0x00, // status
0x01, 0x00, // connection handle: 1
0x00 // encryption disabled
);
auto kEncryptionChangeEventFailed = CreateStaticByteBuffer(
0x08, // HCI Encryption Change event code
4, // parameter total size
0x06, // status: Pin or Key missing
0x01, 0x00, // connection handle: 1
0x00 // encryption disabled
);
auto kKeySizeComplete = CreateStaticByteBuffer(
0x0E, // event code: Command Complete
0x07, // parameters total size
0xFF, // num command packets allowed (255)
0x08, 0x14, // original opcode
// return parameters
0x00, // status (success)
0x01, 0x00, // connection handle: 0x0001
0x10 // encryption key size: 16
);
// clang-format on
int callback_count = 0;
auto conn = NewConnection();
Status status(HostError::kFailed);
bool enabled = false;
conn->set_encryption_change_callback([&](Status cb_status, bool cb_enabled) {
callback_count++;
status = cb_status;
enabled = cb_enabled;
});
if (conn->ll_type() == Connection::LinkType::kACL) {
// The host tries to validate the size of key used to encrypt ACL links.
test_device()->QueueCommandTransaction(kReadEncryptionKeySizeCommand,
{&kKeySizeComplete});
}
test_device()->SendCommandChannelPacket(kEncryptionChangeEventEnabled);
RunLoopUntilIdle();
EXPECT_EQ(1, callback_count);
EXPECT_TRUE(status);
EXPECT_TRUE(enabled);
test_device()->SendCommandChannelPacket(kEncryptionChangeEventDisabled);
RunLoopUntilIdle();
EXPECT_EQ(2, callback_count);
EXPECT_TRUE(status);
EXPECT_FALSE(enabled);
// The host should disconnect the link if encryption fails.
test_device()->QueueCommandTransaction(kDisconnectCommand, {});
test_device()->SendCommandChannelPacket(kEncryptionChangeEventFailed);
RunLoopUntilIdle();
EXPECT_EQ(3, callback_count);
EXPECT_FALSE(status);
EXPECT_EQ(StatusCode::kPinOrKeyMissing, status.protocol_error());
}
TEST_F(HCI_ConnectionTest, AclEncryptionEnableCanNotReadKeySizeClosesLink) {
auto kKeySizeComplete =
CreateStaticByteBuffer(0x0E, // event code: Command Complete
0x07, // parameters total size
0xFF, // num command packets allowed (255)
0x08, 0x14, // original opcode
// return parameters
0x2F, // status (insufficient security)
0x01, 0x00, // connection handle: 0x0001
0x10 // encryption key size: 16
);
int callback_count = 0;
auto conn = NewACLConnection();
conn->set_encryption_change_callback(
[&callback_count](Status status, bool enabled) {
callback_count++;
EXPECT_FALSE(status);
EXPECT_TRUE(enabled);
});
test_device()->QueueCommandTransaction(kReadEncryptionKeySizeCommand,
{&kKeySizeComplete});
test_device()->QueueCommandTransaction(kDisconnectCommand, {});
test_device()->SendCommandChannelPacket(kEncryptionChangeEventEnabled);
RunLoopUntilIdle();
EXPECT_EQ(1, callback_count);
}
TEST_F(HCI_ConnectionTest, AclEncryptionEnableKeySizeOneByteClosesLink) {
auto kKeySizeComplete =
CreateStaticByteBuffer(0x0E, // event code: Command Complete
0x07, // parameters total size
0xFF, // num command packets allowed (255)
0x08, 0x14, // original opcode
// return parameters
0x00, // status (success)
0x01, 0x00, // connection handle: 0x0001
0x01 // encryption key size: 1
);
int callback_count = 0;
auto conn = NewACLConnection();
conn->set_encryption_change_callback(
[&callback_count](Status status, bool enabled) {
callback_count++;
EXPECT_FALSE(status);
EXPECT_TRUE(enabled);
});
test_device()->QueueCommandTransaction(kReadEncryptionKeySizeCommand,
{&kKeySizeComplete});
test_device()->QueueCommandTransaction(kDisconnectCommand, {});
test_device()->SendCommandChannelPacket(kEncryptionChangeEventEnabled);
RunLoopUntilIdle();
EXPECT_EQ(1, callback_count);
}
TEST_P(LinkTypeConnectionTest, EncryptionKeyRefreshEvents) {
// clang-format off
auto kEncryptionKeyRefresh = CreateStaticByteBuffer(
0x30, // HCI Encryption Key Refresh Complete event
3, // parameter total size
0x00, // status
0x01, 0x00 // connection handle: 1
);
auto kEncryptionKeyRefreshFailed = CreateStaticByteBuffer(
0x30, // HCI Encryption Key Refresh Complete event
3, // parameter total size
0x06, // status: Pin or Key missing
0x01, 0x00 // connection handle: 1
);
// clang-format on
int callback_count = 0;
auto conn = NewConnection();
Status status(HostError::kFailed);
bool enabled = false;
conn->set_encryption_change_callback([&](Status cb_status, bool cb_enabled) {
callback_count++;
status = cb_status;
enabled = cb_enabled;
});
test_device()->SendCommandChannelPacket(kEncryptionKeyRefresh);
RunLoopUntilIdle();
EXPECT_EQ(1, callback_count);
EXPECT_TRUE(status);
EXPECT_TRUE(enabled);
// The host should disconnect the link if encryption fails.
test_device()->QueueCommandTransaction(kDisconnectCommand, {});
test_device()->SendCommandChannelPacket(kEncryptionKeyRefreshFailed);
RunLoopUntilIdle();
EXPECT_EQ(2, callback_count);
EXPECT_FALSE(status);
EXPECT_EQ(StatusCode::kPinOrKeyMissing, status.protocol_error());
EXPECT_FALSE(enabled);
}
TEST_F(HCI_ConnectionTest, LELongTermKeyRequestIgnoredEvent) {
// clang-format off
auto kMalformed = CreateStaticByteBuffer(
0x3E, // LE Meta Event code
12, // parameter total size
0x05, // LE LTK Request subevent code
0x01, 0x00, // connection handle: 1
// rand:
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ediv: (missing 1 byte)
0x00
);
auto kWrongHandle = CreateStaticByteBuffer(
0x3E, // LE Meta Event code
13, // parameter total size
0x05, // LE LTK Request subevent code
0x02, 0x00, // connection handle: 2 (wrong)
// rand: 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ediv: 0
0x00, 0x00
);
// clang-format on
auto conn = NewLEConnection();
conn->set_link_key(LinkKey(kLTK, 0, 0));
test_device()->SendCommandChannelPacket(kMalformed);
test_device()->SendCommandChannelPacket(kWrongHandle);
RunLoopUntilIdle();
// Test will fail if the connection sends a response without ignoring these
// events.
}
TEST_F(HCI_ConnectionTest, LELongTermKeyRequestNoKey) {
// clang-format off
auto kEvent = CreateStaticByteBuffer(
0x3E, // LE Meta Event code
13, // parameter total size
0x05, // LE LTK Request subevent code
0x01, 0x00, // connection handle: 2 (wrong)
// rand: 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ediv: 0
0x00, 0x00
);
auto kResponse = CreateStaticByteBuffer(
0x1B, 0x20, // opcode: HCI_LE_Long_Term_Key_Request_Negative_Reply
2, // parameter total size
0x01, 0x00 // connection handle: 1
);
// clang-format on
// The request should be rejected since there is no LTK.
test_device()->QueueCommandTransaction(kResponse, {});
auto conn = NewLEConnection();
test_device()->SendCommandChannelPacket(kEvent);
RunLoopUntilIdle();
}
// There is a link key but EDiv and Rand values don't match.
TEST_F(HCI_ConnectionTest, LELongTermKeyRequestNoMatchinKey) {
// clang-format off
auto kEvent = CreateStaticByteBuffer(
0x3E, // LE Meta Event code
13, // parameter total size
0x05, // LE LTK Request subevent code
0x01, 0x00, // connection handle: 2 (wrong)
// rand: 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ediv: 0
0x00, 0x00
);
auto kResponse = CreateStaticByteBuffer(
0x1B, 0x20, // opcode: HCI_LE_Long_Term_Key_Request_Negative_Reply
2, // parameter total size
0x01, 0x00 // connection handle: 1
);
// clang-format on
// The request should be rejected since there is no LTK.
test_device()->QueueCommandTransaction(kResponse, {});
auto conn = NewLEConnection();
conn->set_link_key(LinkKey(kLTK, 1, 1));
test_device()->SendCommandChannelPacket(kEvent);
RunLoopUntilIdle();
}
TEST_F(HCI_ConnectionTest, LELongTermKeyRequestReply) {
// clang-format off
auto kEvent = CreateStaticByteBuffer(
0x3E, // LE Meta Event code
13, // parameter total size
0x05, // LE LTK Request subevent code
0x01, 0x00, // connection handle: 2 (wrong)
// rand: 0x8899AABBCCDDEEFF
0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
// ediv: 0xBEEF
0xEF, 0xBE
);
auto kResponse = CreateStaticByteBuffer(
0x1A, 0x20, // opcode: HCI_LE_Long_Term_Key_Request_Reply
18, // parameter total size
0x01, 0x00, // connection handle: 1
// LTK:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
// The request should be rejected since there is no LTK.
test_device()->QueueCommandTransaction(kResponse, {});
auto conn = NewLEConnection();
conn->set_link_key(LinkKey(kLTK, 0x8899AABBCCDDEEFF, 0xBEEF));
test_device()->SendCommandChannelPacket(kEvent);
RunLoopUntilIdle();
}
// Tests that a Connection clears the ACL data channel state associated with its
// connection handle during destruction.
TEST_F(HCI_ConnectionTest, ClearAclState) {
constexpr size_t kMaxNumPackets = 1;
ASSERT_EQ(kMaxNumPackets, kLeBufferInfo.max_num_packets());
auto conn = NewLEConnection();
// TODO(NET-1211): Change this test to exercise enable/disable functionality.
// Consider creating a FakeAclDataChannel class to prevent duplicating tests
// in HCI_ConnectionTest and HCI_ACLDataChannelTest.
size_t packet_count = 0;
test_device()->SetDataCallback([&](const auto&) { packet_count++; },
dispatcher());
ASSERT_TRUE(acl_data_channel()->SendPacket(
ACLDataPacket::New(conn->handle(),
ACLPacketBoundaryFlag::kFirstNonFlushable,
ACLBroadcastFlag::kPointToPoint, 1),
Connection::LinkType::kLE));
ASSERT_TRUE(acl_data_channel()->SendPacket(
ACLDataPacket::New(conn->handle(),
ACLPacketBoundaryFlag::kFirstNonFlushable,
ACLBroadcastFlag::kPointToPoint, 1),
Connection::LinkType::kLE));
RunLoopUntilIdle();
// The second packet should have been queued.
ASSERT_EQ(kMaxNumPackets, packet_count);
// Mark the connection as closed so that destroying it doesn't send
// HCI_Disconnect. ACLDataChannel should get updated regardless.
conn->set_closed();
// This should allow the next packet to go out.
conn = nullptr;
RunLoopUntilIdle();
ASSERT_EQ(2u, packet_count);
}
// Test connection handling cases for all types of links.
INSTANTIATE_TEST_SUITE_P(HCI_ConnectionTest, LinkTypeConnectionTest,
::testing::Values(Connection::LinkType::kACL,
Connection::LinkType::kLE));
} // namespace
} // namespace hci
} // namespace bt