blob: e619b545a154702774f5f27037b146c0e9d03519 [file] [log] [blame]
// 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