| // Copyright 2020 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/gap/bredr_interrogator.h" |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/peer_cache.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/util.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/fake_l2cap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.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_peer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/mock_controller.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/error.h" |
| |
| namespace bt::gap { |
| |
| constexpr hci_spec::ConnectionHandle kConnectionHandle = 0x0BAA; |
| const DeviceAddress kTestDevAddr(DeviceAddress::Type::kBREDR, {1}); |
| |
| const auto kRemoteNameRequestRsp = testing::CommandStatusPacket( |
| hci_spec::kRemoteNameRequest, pw::bluetooth::emboss::StatusCode::SUCCESS); |
| |
| const auto kReadRemoteVersionInfoRsp = |
| testing::CommandStatusPacket(hci_spec::kReadRemoteVersionInfo, |
| pw::bluetooth::emboss::StatusCode::SUCCESS); |
| |
| const auto kReadRemoteSupportedFeaturesRsp = |
| testing::CommandStatusPacket(hci_spec::kReadRemoteSupportedFeatures, |
| pw::bluetooth::emboss::StatusCode::SUCCESS); |
| |
| const auto kReadRemoteExtendedFeaturesRsp = |
| testing::CommandStatusPacket(hci_spec::kReadRemoteExtendedFeatures, |
| pw::bluetooth::emboss::StatusCode::SUCCESS); |
| |
| using bt::testing::CommandTransaction; |
| |
| using TestingBase = |
| bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>; |
| |
| class BrEdrInterrogatorTest : public TestingBase { |
| public: |
| BrEdrInterrogatorTest() = default; |
| ~BrEdrInterrogatorTest() override = default; |
| |
| void SetUp() override { |
| TestingBase::SetUp(); |
| |
| peer_cache_ = std::make_unique<PeerCache>(dispatcher()); |
| peer_ = peer_cache()->NewPeer(kTestDevAddr, /*connectable=*/true); |
| EXPECT_FALSE(peer_->name()); |
| EXPECT_FALSE(peer_->version()); |
| EXPECT_FALSE(peer_->features().HasPage(0)); |
| EXPECT_FALSE(peer_->features().HasBit( |
| /*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)); |
| EXPECT_EQ(0u, peer_->features().last_page_number()); |
| |
| interrogator_ = std::make_unique<BrEdrInterrogator>( |
| peer_->GetWeakPtr(), kConnectionHandle, cmd_channel()->AsWeakPtr()); |
| } |
| |
| void TearDown() override { |
| RunUntilIdle(); |
| test_device()->Stop(); |
| interrogator_ = nullptr; |
| peer_cache_ = nullptr; |
| TestingBase::TearDown(); |
| } |
| |
| protected: |
| void QueueSuccessfulInterrogation(DeviceAddress addr, |
| hci_spec::ConnectionHandle conn, |
| bool extended_features = true) const { |
| const DynamicByteBuffer remote_name_request_complete_packet = |
| testing::RemoteNameRequestCompletePacket(addr); |
| const DynamicByteBuffer remote_version_complete_packet = |
| testing::ReadRemoteVersionInfoCompletePacket(conn); |
| const DynamicByteBuffer remote_supported_complete_packet = |
| testing::ReadRemoteSupportedFeaturesCompletePacket(conn, |
| extended_features); |
| |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::RemoteNameRequestPacket(addr), |
| &kRemoteNameRequestRsp, |
| &remote_name_request_complete_packet); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::ReadRemoteVersionInfoPacket(conn), |
| &kReadRemoteVersionInfoRsp, |
| &remote_version_complete_packet); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::ReadRemoteSupportedFeaturesPacket(conn), |
| &kReadRemoteSupportedFeaturesRsp, |
| &remote_supported_complete_packet); |
| if (extended_features) { |
| QueueSuccessfulReadRemoteExtendedFeatures(conn); |
| } |
| } |
| |
| void QueueSuccessfulReadRemoteExtendedFeatures( |
| hci_spec::ConnectionHandle conn) const { |
| const DynamicByteBuffer remote_extended1_complete_packet = |
| testing::ReadRemoteExtended1CompletePacket(conn); |
| const DynamicByteBuffer remote_extended2_complete_packet = |
| testing::ReadRemoteExtended2CompletePacket(conn); |
| |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::ReadRemoteExtended1Packet(conn), |
| &kReadRemoteExtendedFeaturesRsp, |
| &remote_extended1_complete_packet); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::ReadRemoteExtended2Packet(conn), |
| &kReadRemoteExtendedFeaturesRsp, |
| &remote_extended2_complete_packet); |
| } |
| |
| void DestroyInterrogator() { interrogator_.reset(); } |
| |
| Peer* peer() const { return peer_; } |
| |
| PeerCache* peer_cache() const { return peer_cache_.get(); } |
| |
| BrEdrInterrogator* interrogator() const { return interrogator_.get(); } |
| |
| private: |
| Peer* peer_ = nullptr; |
| std::unique_ptr<PeerCache> peer_cache_; |
| std::unique_ptr<BrEdrInterrogator> interrogator_; |
| |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BrEdrInterrogatorTest); |
| }; |
| |
| using GAP_BrEdrInterrogatorTest = BrEdrInterrogatorTest; |
| |
| TEST_F(BrEdrInterrogatorTest, |
| InterrogationFailsWithMalformedRemoteNameRequestComplete) { |
| // Remote Name Request Complete event with insufficient length. |
| const auto addr = kTestDevAddr.value().bytes(); |
| StaticByteBuffer remote_name_request_complete_packet( |
| hci_spec::kRemoteNameRequestCompleteEventCode, |
| 0x08, // parameter_total_size (8) |
| pw::bluetooth::emboss::StatusCode::SUCCESS, // status |
| addr[0], |
| addr[1], |
| addr[2], |
| addr[3], |
| addr[4], |
| addr[5], // peer address |
| 'F' // remote name |
| ); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::RemoteNameRequestPacket(kTestDevAddr), |
| &kRemoteNameRequestRsp, |
| &remote_name_request_complete_packet); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), testing::ReadRemoteVersionInfoPacket(kConnectionHandle)); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), |
| testing::ReadRemoteSupportedFeaturesPacket(kConnectionHandle)); |
| |
| hci::Result<> status = fit::ok(); |
| interrogator()->Start( |
| [&status](hci::Result<> cb_status) { status = cb_status; }); |
| RunUntilIdle(); |
| EXPECT_TRUE(status.is_error()); |
| } |
| |
| TEST_F(BrEdrInterrogatorTest, SuccessfulInterrogation) { |
| QueueSuccessfulInterrogation(kTestDevAddr, kConnectionHandle); |
| |
| std::optional<hci::Result<>> status; |
| interrogator()->Start( |
| [&status](hci::Result<> cb_status) { status = cb_status; }); |
| RunUntilIdle(); |
| |
| ASSERT_TRUE(status.has_value()); |
| EXPECT_EQ(fit::ok(), *status); |
| |
| EXPECT_TRUE(peer()->name()); |
| EXPECT_TRUE(peer()->version()); |
| EXPECT_TRUE(peer()->features().HasPage(0)); |
| EXPECT_TRUE(peer()->features().HasBit( |
| /*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)); |
| EXPECT_EQ(2u, peer()->features().last_page_number()); |
| } |
| |
| TEST_F(BrEdrInterrogatorTest, SuccessfulReinterrogation) { |
| QueueSuccessfulInterrogation(kTestDevAddr, kConnectionHandle); |
| |
| std::optional<hci::Result<>> status; |
| interrogator()->Start( |
| [&status](hci::Result<> cb_status) { status = cb_status; }); |
| RunUntilIdle(); |
| |
| ASSERT_TRUE(status.has_value()); |
| EXPECT_EQ(fit::ok(), *status); |
| status = std::nullopt; |
| |
| QueueSuccessfulReadRemoteExtendedFeatures(kConnectionHandle); |
| interrogator()->Start( |
| [&status](hci::Result<> cb_status) { status = cb_status; }); |
| RunUntilIdle(); |
| ASSERT_TRUE(status.has_value()); |
| EXPECT_EQ(fit::ok(), *status); |
| } |
| |
| TEST_F(BrEdrInterrogatorTest, InterrogationFailedToGetName) { |
| const DynamicByteBuffer remote_name_request_failure_rsp = |
| testing::CommandStatusPacket( |
| hci_spec::kRemoteNameRequest, |
| pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR); |
| EXPECT_CMD_PACKET_OUT(test_device(), |
| testing::RemoteNameRequestPacket(kTestDevAddr), |
| &remote_name_request_failure_rsp); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), testing::ReadRemoteVersionInfoPacket(kConnectionHandle)); |
| EXPECT_CMD_PACKET_OUT( |
| test_device(), |
| testing::ReadRemoteSupportedFeaturesPacket(kConnectionHandle)); |
| |
| std::optional<hci::Result<>> status; |
| interrogator()->Start( |
| [&status](hci::Result<> cb_status) { status = cb_status; }); |
| RunUntilIdle(); |
| |
| ASSERT_TRUE(status.has_value()); |
| EXPECT_FALSE(status->is_ok()); |
| } |
| |
| TEST_F(BrEdrInterrogatorTest, Cancel) { |
| QueueSuccessfulInterrogation( |
| kTestDevAddr, kConnectionHandle, /*extended_features=*/false); |
| |
| std::optional<hci::Result<>> result; |
| interrogator()->Start([&](hci::Result<> status) { result = status; }); |
| EXPECT_FALSE(result.has_value()); |
| |
| interrogator()->Cancel(); |
| |
| // The result callback should be called synchronously. |
| ASSERT_TRUE(result.has_value()); |
| EXPECT_EQ(result.value(), ToResult(HostError::kCanceled)); |
| result.reset(); |
| |
| // Events should be ignored. |
| RunUntilIdle(); |
| EXPECT_FALSE(result.has_value()); |
| } |
| |
| TEST_F(BrEdrInterrogatorTest, InterrogatorDestroyedInCompleteCallback) { |
| QueueSuccessfulInterrogation(kTestDevAddr, kConnectionHandle); |
| |
| std::optional<hci::Result<>> status; |
| interrogator()->Start([this, &status](hci::Result<> cb_status) { |
| status = cb_status; |
| DestroyInterrogator(); |
| }); |
| RunUntilIdle(); |
| |
| ASSERT_TRUE(status.has_value()); |
| EXPECT_TRUE(status->is_ok()); |
| |
| EXPECT_TRUE(peer()->name()); |
| EXPECT_TRUE(peer()->version()); |
| EXPECT_TRUE(peer()->features().HasPage(0)); |
| EXPECT_TRUE(peer()->features().HasBit( |
| /*page=*/0, hci_spec::LMPFeature::kExtendedFeatures)); |
| EXPECT_EQ(2u, peer()->features().last_page_number()); |
| } |
| } // namespace bt::gap |