| // 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/hci/low_energy_connector.h" |
| |
| #include <vector> |
| |
| #include "pw_async/heap_dispatcher.h" |
| #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/defaults.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/fake_local_address_delegate.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/fake_low_energy_connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/controller_test.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_controller.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_peer.h" |
| |
| namespace bt::hci { |
| namespace { |
| |
| using bt::testing::FakeController; |
| using bt::testing::FakePeer; |
| using TestingBase = bt::testing::FakeDispatcherControllerTest<FakeController>; |
| |
| const DeviceAddress kLocalAddress(DeviceAddress::Type::kLEPublic, {0xFF}); |
| const DeviceAddress kRandomAddress(DeviceAddress::Type::kLERandom, {0xFE}); |
| const DeviceAddress kTestAddress(DeviceAddress::Type::kLEPublic, {1}); |
| const hci_spec::LEPreferredConnectionParameters kTestParams(6, 6, 1, 10); |
| constexpr pw::chrono::SystemClock::duration kPwConnectTimeout = |
| std::chrono::seconds(10); |
| |
| class LowEnergyConnectorTest : public TestingBase, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| LowEnergyConnectorTest() = default; |
| ~LowEnergyConnectorTest() override = default; |
| |
| protected: |
| void SetUp() override { |
| TestingBase::SetUp(); |
| InitializeACLDataChannel(); |
| |
| FakeController::Settings settings; |
| settings.ApplyLegacyLEConfig(); |
| test_device()->set_settings(settings); |
| |
| fake_address_delegate_.set_local_address(kLocalAddress); |
| |
| bool use_extended_operations = GetParam(); |
| connector_ = std::make_unique<LowEnergyConnector>( |
| transport()->GetWeakPtr(), |
| &fake_address_delegate_, |
| dispatcher(), |
| fit::bind_member<&LowEnergyConnectorTest::OnIncomingConnectionCreated>( |
| this), |
| use_extended_operations); |
| |
| test_device()->set_connection_state_callback( |
| fit::bind_member<&LowEnergyConnectorTest::OnConnectionStateChanged>( |
| this)); |
| } |
| |
| void TearDown() override { |
| connector_ = nullptr; |
| in_connections_.clear(); |
| test_device()->Stop(); |
| TestingBase::TearDown(); |
| } |
| |
| EmbossEventPacket CreateConnectionCompleteSubevent( |
| hci_spec::ConnectionHandle conn_handle, |
| const DeviceAddress& peer_address) const { |
| auto packet = hci::EmbossEventPacket::New< |
| pw::bluetooth::emboss::LEEnhancedConnectionCompleteSubeventV1Writer>( |
| hci_spec::kLEMetaEventCode); |
| auto params = packet.view_t(); |
| |
| params.le_meta_event().subevent_code().Write( |
| hci_spec::kLEEnhancedConnectionCompleteSubeventCode); |
| params.status().Write(pw::bluetooth::emboss::StatusCode::SUCCESS); |
| params.connection_handle().Write(conn_handle); |
| params.role().Write(pw::bluetooth::emboss::ConnectionRole::PERIPHERAL); |
| params.peer_address_type().Write( |
| pw::bluetooth::emboss::LEAddressType::PUBLIC); |
| params.peer_address().CopyFrom(peer_address.value().view()); |
| params.connection_interval().Write( |
| hci_spec::defaults::kLEConnectionIntervalMin); |
| params.peripheral_latency().Write(0); |
| params.supervision_timeout().Write(10); |
| params.central_clock_accuracy().Write( |
| pw::bluetooth::emboss::LEClockAccuracy::PPM_20); |
| |
| // there are also local_resolvable_private_address and |
| // peer_resolvable_private_address fields available within |
| // LEEnhancedConnectionCompleteSubeventV1. However, these fields are only |
| // valid when we use either the identity addresses in |
| // HCI_LE_Enhanced_Create_Connection for either the local device or peer |
| // device. Our tests mainly use public or random addresses, as if they |
| // have already resolved the addresses. We ignore setting these fields |
| // here to prevent confusion. |
| |
| return packet; |
| } |
| |
| pw::async::HeapDispatcher& heap_dispatcher() { return heap_dispatcher_; } |
| void DeleteConnector() { connector_ = nullptr; } |
| bool request_canceled() const { return request_canceled_; } |
| LowEnergyConnector* connector() const { return connector_.get(); } |
| |
| const std::vector<std::unique_ptr<LowEnergyConnection>>& in_connections() |
| const { |
| return in_connections_; |
| } |
| |
| FakeLocalAddressDelegate* fake_address_delegate() { |
| return &fake_address_delegate_; |
| } |
| |
| private: |
| void OnIncomingConnectionCreated( |
| hci_spec::ConnectionHandle handle, |
| pw::bluetooth::emboss::ConnectionRole role, |
| const DeviceAddress& peer_address, |
| const hci_spec::LEConnectionParameters& conn_params) { |
| in_connections_.push_back( |
| std::make_unique<testing::FakeLowEnergyConnection>( |
| handle, |
| kLocalAddress, |
| peer_address, |
| role, |
| transport()->GetWeakPtr())); |
| } |
| |
| void OnConnectionStateChanged(const DeviceAddress& address, |
| hci_spec::ConnectionHandle handle, |
| bool connected, |
| bool canceled) { |
| request_canceled_ = canceled; |
| } |
| |
| bool request_canceled_ = false; |
| FakeLocalAddressDelegate fake_address_delegate_{dispatcher()}; |
| std::unique_ptr<LowEnergyConnector> connector_; |
| std::vector<std::unique_ptr<LowEnergyConnection>> in_connections_; |
| pw::async::HeapDispatcher heap_dispatcher_{dispatcher()}; |
| |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnectorTest); |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(LowEnergyConnectorTest, |
| LowEnergyConnectorTest, |
| ::testing::Bool()); |
| |
| TEST_P(LowEnergyConnectorTest, CreateConnection) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_FALSE(connector()->pending_peer_address()); |
| |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| bool callback_called = false; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| conn = std::move(cb_conn); |
| callback_called = true; |
| }; |
| |
| bool ret = connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_TRUE(ret); |
| EXPECT_TRUE(connector()->request_pending()); |
| EXPECT_EQ(connector()->pending_peer_address().value(), kTestAddress); |
| |
| ret = connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_FALSE(ret); |
| |
| RunUntilIdle(); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_FALSE(connector()->pending_peer_address()); |
| EXPECT_TRUE(callback_called); |
| EXPECT_EQ(fit::ok(), status); |
| EXPECT_TRUE(in_connections().empty()); |
| |
| ASSERT_TRUE(conn); |
| EXPECT_EQ(1u, conn->handle()); |
| EXPECT_EQ(kLocalAddress, conn->local_address()); |
| EXPECT_EQ(kTestAddress, conn->peer_address()); |
| conn->Disconnect( |
| pw::bluetooth::emboss::StatusCode::REMOTE_USER_TERMINATED_CONNECTION); |
| } |
| |
| // Controller reports error from HCI Command Status event. |
| TEST_P(LowEnergyConnectorTest, CreateConnectionStatusError) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| fake_peer->set_connect_status( |
| pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| bool callback_called = false; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| conn = std::move(cb_conn); |
| callback_called = true; |
| }; |
| |
| bool ret = connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_TRUE(ret); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| RunUntilIdle(); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_TRUE(callback_called); |
| EXPECT_EQ(ToResult(pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED), |
| status); |
| EXPECT_FALSE(conn); |
| EXPECT_TRUE(in_connections().empty()); |
| } |
| |
| // Controller reports error from HCI LE Connection Complete event |
| TEST_P(LowEnergyConnectorTest, CreateConnectionEventError) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| fake_peer->set_connect_response( |
| pw::bluetooth::emboss::StatusCode::CONNECTION_REJECTED_SECURITY); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| bool callback_called = false; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| callback_called = true; |
| conn = std::move(cb_conn); |
| }; |
| |
| bool ret = connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_TRUE(ret); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| RunUntilIdle(); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_TRUE(callback_called); |
| EXPECT_EQ( |
| ToResult(pw::bluetooth::emboss::StatusCode::CONNECTION_REJECTED_SECURITY), |
| status); |
| EXPECT_TRUE(in_connections().empty()); |
| EXPECT_FALSE(conn); |
| } |
| |
| // Controller reports error from HCI LE Connection Complete event |
| TEST_P(LowEnergyConnectorTest, Cancel) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| |
| // Make sure the pending connect remains pending. |
| fake_peer->set_force_pending_connect(true); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| hci::Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| bool callback_called = false; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| callback_called = true; |
| conn = std::move(cb_conn); |
| }; |
| |
| bool ret = connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_TRUE(ret); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| ASSERT_FALSE(request_canceled()); |
| |
| connector()->Cancel(); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| // The request timeout should be canceled regardless of whether it was posted |
| // before. |
| EXPECT_FALSE(connector()->timeout_posted()); |
| |
| RunUntilIdle(); |
| |
| EXPECT_FALSE(connector()->timeout_posted()); |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_TRUE(callback_called); |
| EXPECT_TRUE(request_canceled()); |
| EXPECT_EQ(ToResult(HostError::kCanceled), status); |
| EXPECT_TRUE(in_connections().empty()); |
| EXPECT_FALSE(conn); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, IncomingConnect) { |
| EXPECT_TRUE(in_connections().empty()); |
| EXPECT_FALSE(connector()->request_pending()); |
| |
| EmbossEventPacket packet = CreateConnectionCompleteSubevent(1, kTestAddress); |
| test_device()->SendCommandChannelPacket(packet.data()); |
| |
| RunUntilIdle(); |
| ASSERT_EQ(1u, in_connections().size()); |
| |
| auto conn = in_connections()[0].get(); |
| EXPECT_EQ(1u, conn->handle()); |
| EXPECT_EQ(kLocalAddress, conn->local_address()); |
| EXPECT_EQ(kTestAddress, conn->peer_address()); |
| conn->Disconnect( |
| pw::bluetooth::emboss::StatusCode::REMOTE_USER_TERMINATED_CONNECTION); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, IncomingConnectDuringConnectionRequest) { |
| const DeviceAddress kIncomingAddress(DeviceAddress::Type::kLEPublic, {2}); |
| |
| EXPECT_TRUE(in_connections().empty()); |
| EXPECT_FALSE(connector()->request_pending()); |
| |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| unsigned int callback_count = 0; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| callback_count++; |
| conn = std::move(cb_conn); |
| }; |
| |
| connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| |
| (void)heap_dispatcher().Post( |
| [kIncomingAddress, this](pw::async::Context /*ctx*/, pw::Status status) { |
| if (!status.ok()) { |
| return; |
| } |
| |
| EmbossEventPacket packet = |
| CreateConnectionCompleteSubevent(2, kIncomingAddress); |
| test_device()->SendCommandChannelPacket(packet.data()); |
| }); |
| |
| RunUntilIdle(); |
| |
| EXPECT_EQ(fit::ok(), status); |
| EXPECT_EQ(1u, callback_count); |
| ASSERT_EQ(1u, in_connections().size()); |
| |
| const auto& in_conn = in_connections().front(); |
| |
| EXPECT_EQ(1u, conn->handle()); |
| EXPECT_EQ(2u, in_conn->handle()); |
| EXPECT_EQ(kTestAddress, conn->peer_address()); |
| EXPECT_EQ(kIncomingAddress, in_conn->peer_address()); |
| |
| conn->Disconnect( |
| pw::bluetooth::emboss::StatusCode::REMOTE_USER_TERMINATED_CONNECTION); |
| in_conn->Disconnect( |
| pw::bluetooth::emboss::StatusCode::REMOTE_USER_TERMINATED_CONNECTION); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, CreateConnectionTimeout) { |
| // We do not set up any fake devices. This will cause the request to time out. |
| EXPECT_FALSE(connector()->request_pending()); |
| |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| bool callback_called = false; |
| |
| auto callback = [&](auto cb_status, auto cb_conn) { |
| status = cb_status; |
| callback_called = true; |
| conn = std::move(cb_conn); |
| }; |
| |
| connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| callback, |
| kPwConnectTimeout); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| EXPECT_FALSE(request_canceled()); |
| |
| // Make the connection attempt time out. |
| RunFor(kPwConnectTimeout); |
| |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_TRUE(callback_called); |
| EXPECT_TRUE(request_canceled()); |
| EXPECT_EQ(ToResult(HostError::kTimedOut), status); |
| EXPECT_TRUE(in_connections().empty()); |
| EXPECT_FALSE(conn); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, SendRequestAndDelete) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| |
| // Make sure the pending connect remains pending. |
| fake_peer->set_force_pending_connect(true); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| bool ret = connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| EXPECT_TRUE(ret); |
| EXPECT_TRUE(connector()->request_pending()); |
| |
| DeleteConnector(); |
| RunUntilIdle(); |
| |
| EXPECT_TRUE(request_canceled()); |
| EXPECT_TRUE(in_connections().empty()); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, AllowsRandomAddressChange) { |
| EXPECT_TRUE(connector()->AllowsRandomAddressChange()); |
| |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| // Address change should not be allowed while the procedure is pending. |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| EXPECT_TRUE(connector()->request_pending()); |
| EXPECT_FALSE(connector()->AllowsRandomAddressChange()); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(connector()->AllowsRandomAddressChange()); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, |
| AllowsRandomAddressChangeWhileRequestingLocalAddress) { |
| // Make the local address delegate report its result asynchronously. |
| fake_address_delegate()->set_async(true); |
| |
| // The connector should be in the "request pending" state without initiating |
| // controller procedures that would prevent a local address change. |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| EXPECT_TRUE(connector()->request_pending()); |
| EXPECT_TRUE(connector()->AllowsRandomAddressChange()); |
| |
| // Initiating a new connection should fail in this state. |
| bool result = connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| EXPECT_FALSE(result); |
| |
| // After the loop runs the request should remain pending (since we added no |
| // fake device, the request would eventually timeout) but address change |
| // should no longer be allowed. |
| RunUntilIdle(); |
| EXPECT_TRUE(connector()->request_pending()); |
| EXPECT_FALSE(connector()->AllowsRandomAddressChange()); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, ConnectUsingAcceptList) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| true, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| ASSERT_TRUE(test_device()->le_connect_params()); |
| EXPECT_EQ(pw::bluetooth::emboss::LEOwnAddressType::PUBLIC, |
| test_device()->le_connect_params()->own_address_type); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, ConnectUsingPublicAddress) { |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| ASSERT_TRUE(test_device()->le_connect_params()); |
| EXPECT_EQ(pw::bluetooth::emboss::LEOwnAddressType::PUBLIC, |
| test_device()->le_connect_params()->own_address_type); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, ConnectUsingRandomAddress) { |
| fake_address_delegate()->set_local_address(kRandomAddress); |
| |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| ASSERT_TRUE(test_device()->le_connect_params()); |
| EXPECT_EQ(pw::bluetooth::emboss::LEOwnAddressType::RANDOM, |
| test_device()->le_connect_params()->own_address_type); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, ConnectUsingRandomPeerAddress) { |
| auto fake_peer = std::make_unique<FakePeer>(kRandomAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kRandomAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| ASSERT_TRUE(test_device()->le_connect_params()); |
| EXPECT_EQ(DeviceAddress::Type::kLERandom, |
| test_device()->le_connect_params()->peer_address.type()); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, CancelConnectWhileWaitingForLocalAddress) { |
| Result<> status = fit::ok(); |
| std::unique_ptr<LowEnergyConnection> conn; |
| auto callback = [&](auto s, auto c) { |
| status = s; |
| conn = std::move(c); |
| }; |
| fake_address_delegate()->set_async(true); |
| connector()->CreateConnection( |
| /*use_accept_list=*/false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| std::move(callback), |
| kPwConnectTimeout); |
| |
| // Should be waiting for the address. |
| EXPECT_TRUE(connector()->request_pending()); |
| EXPECT_TRUE(connector()->AllowsRandomAddressChange()); |
| |
| connector()->Cancel(); |
| RunUntilIdle(); |
| EXPECT_FALSE(connector()->request_pending()); |
| EXPECT_TRUE(connector()->AllowsRandomAddressChange()); |
| |
| // The controller should have received no command from us. |
| EXPECT_FALSE(test_device()->le_connect_params()); |
| EXPECT_FALSE(request_canceled()); |
| |
| // Our request should have resulted in an error. |
| EXPECT_EQ(ToResult(HostError::kCanceled), status); |
| EXPECT_FALSE(conn); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, UseLocalIdentityAddress) { |
| // Public identity address and a random current local address. |
| fake_address_delegate()->set_identity_address(kLocalAddress); |
| fake_address_delegate()->set_local_address(kRandomAddress); |
| |
| connector()->UseLocalIdentityAddress(); |
| |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| false, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| ASSERT_TRUE(test_device()->le_connect_params()); |
| |
| // The public address should have been used. |
| EXPECT_EQ(pw::bluetooth::emboss::LEOwnAddressType::PUBLIC, |
| test_device()->le_connect_params()->own_address_type); |
| } |
| |
| TEST_P(LowEnergyConnectorTest, ExtendedCreateConnectionUsesSameParameters) { |
| bool use_extended_operations = GetParam(); |
| if (!use_extended_operations) { |
| // For this specific test, we want to test only the LE Extended Create |
| // Connection functionality. Since this is only a single test, skipping the |
| // LE Create Connection option seemed simpler. If tests for LE Extended |
| // Create Connection grow in number, we should create a separate file, test |
| // suite, etc. |
| return; |
| } |
| |
| using LEConnectParams = FakeController::LEConnectParams; |
| using InitiatingPHYs = LEConnectParams::InitiatingPHYs; |
| |
| auto fake_peer = std::make_unique<FakePeer>(kTestAddress, dispatcher()); |
| test_device()->AddPeer(std::move(fake_peer)); |
| |
| connector()->CreateConnection( |
| /*use_accept_list=*/ |
| true, |
| kTestAddress, |
| hci_spec::defaults::kLEScanInterval, |
| hci_spec::defaults::kLEScanWindow, |
| kTestParams, |
| [](auto, auto) {}, |
| kPwConnectTimeout); |
| RunUntilIdle(); |
| |
| const std::optional<LEConnectParams>& params = |
| test_device()->le_connect_params(); |
| ASSERT_EQ(3u, params->phy_conn_params.size()); |
| const auto& le_1m = |
| params->phy_conn_params.find(InitiatingPHYs::kLE_1M)->second; |
| const auto& le_2m = |
| params->phy_conn_params.find(InitiatingPHYs::kLE_2M)->second; |
| const auto& le_coded = |
| params->phy_conn_params.find(InitiatingPHYs::kLE_Coded)->second; |
| |
| ASSERT_EQ(le_1m.scan_interval, le_2m.scan_interval); |
| ASSERT_EQ(le_1m.scan_window, le_2m.scan_window); |
| ASSERT_EQ(le_1m.connection_interval_min, le_2m.connection_interval_min); |
| ASSERT_EQ(le_1m.connection_interval_max, le_2m.connection_interval_max); |
| ASSERT_EQ(le_1m.max_latency, le_2m.max_latency); |
| ASSERT_EQ(le_1m.supervision_timeout, le_2m.supervision_timeout); |
| ASSERT_EQ(le_1m.min_ce_length, le_2m.min_ce_length); |
| ASSERT_EQ(le_1m.max_ce_length, le_2m.max_ce_length); |
| |
| ASSERT_EQ(le_2m.scan_interval, le_coded.scan_interval); |
| ASSERT_EQ(le_2m.scan_window, le_coded.scan_window); |
| ASSERT_EQ(le_2m.connection_interval_min, le_coded.connection_interval_min); |
| ASSERT_EQ(le_2m.connection_interval_max, le_coded.connection_interval_max); |
| ASSERT_EQ(le_2m.max_latency, le_coded.max_latency); |
| ASSERT_EQ(le_2m.supervision_timeout, le_coded.supervision_timeout); |
| ASSERT_EQ(le_2m.min_ce_length, le_coded.min_ce_length); |
| ASSERT_EQ(le_2m.max_ce_length, le_coded.max_ce_length); |
| } |
| |
| } // namespace |
| } // namespace bt::hci |