blob: 1017278148667a9f9efb3d23d26c39c05d88732a [file] [log] [blame]
// Copyright 2019 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 "profile_server.h"
#include <fuchsia/bluetooth/bredr/cpp/fidl_test_base.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/connectivity/bluetooth/core/bt-host/data/fake_domain.h"
#include "src/connectivity/bluetooth/core/bt-host/fidl/adapter_test_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/fidl/helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/fake_pairing_delegate.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/fake_peer.h"
namespace bthost {
namespace {
namespace fidlbredr = fuchsia::bluetooth::bredr;
namespace {
const bt::DeviceAddress kTestDevAddr(bt::DeviceAddress::Type::kBREDR, {1});
constexpr bt::l2cap::PSM kPSM = bt::l2cap::kAVDTP;
fidlbredr::ServiceDefinition MakeFIDLServiceDefinition() {
fidlbredr::ServiceDefinition def;
def.mutable_service_class_uuids()->emplace_back(
fidl_helpers::UuidToFidl(bt::sdp::profile::kAudioSink));
fidlbredr::ProtocolDescriptor l2cap_proto;
l2cap_proto.protocol = fidlbredr::ProtocolIdentifier::L2CAP;
fidlbredr::DataElement l2cap_data_el;
l2cap_data_el.set_uint16(fidlbredr::PSM_AVDTP);
l2cap_proto.params.emplace_back(std::move(l2cap_data_el));
def.mutable_protocol_descriptor_list()->emplace_back(std::move(l2cap_proto));
fidlbredr::ProtocolDescriptor avdtp_proto;
avdtp_proto.protocol = fidlbredr::ProtocolIdentifier::AVDTP;
fidlbredr::DataElement avdtp_data_el;
avdtp_data_el.set_uint16(0x0103); // Version 1.3
avdtp_proto.params.emplace_back(std::move(avdtp_data_el));
def.mutable_protocol_descriptor_list()->emplace_back(std::move(avdtp_proto));
fidlbredr::ProfileDescriptor prof_desc;
prof_desc.profile_id = fidlbredr::ServiceClassProfileIdentifier::ADVANCED_AUDIO_DISTRIBUTION;
prof_desc.major_version = 1;
prof_desc.minor_version = 3;
def.mutable_profile_descriptors()->emplace_back(prof_desc);
return def;
}
} // namespace
using TestingBase = bthost::testing::AdapterTestFixture;
class FIDL_ProfileServerTest : public TestingBase {
public:
FIDL_ProfileServerTest() = default;
~FIDL_ProfileServerTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
fidl::InterfaceHandle<fidlbredr::Profile> profile_handle;
client_.Bind(std::move(profile_handle));
server_ =
std::make_unique<ProfileServer>(adapter()->AsWeakPtr(), client_.NewRequest(dispatcher()));
}
void TearDown() override {
RunLoopUntilIdle();
client_ = nullptr;
server_ = nullptr;
TestingBase::TearDown();
}
ProfileServer* server() const { return server_.get(); }
fidlbredr::ProfilePtr& client() { return client_; }
bt::gap::PeerCache* peer_cache() const { return adapter()->peer_cache(); }
bt::gap::BrEdrConnectionManager* conn_mgr() const {
return adapter()->bredr_connection_manager();
}
private:
std::unique_ptr<ProfileServer> server_;
fidlbredr::ProfilePtr client_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FIDL_ProfileServerTest);
};
class MockConnectionReceiver : public fidlbredr::testing::ConnectionReceiver_TestBase {
public:
MockConnectionReceiver(fidl::InterfaceRequest<ConnectionReceiver> request,
async_dispatcher_t* dispatcher)
: binding_(this, std::move(request), dispatcher) {}
~MockConnectionReceiver() override = default;
MOCK_METHOD(void, Connected,
(fuchsia::bluetooth::PeerId peer_id, fidlbredr::Channel channel,
std::vector<fidlbredr::ProtocolDescriptor> protocol),
(override));
private:
fidl::Binding<ConnectionReceiver> binding_;
void NotImplemented_(const std::string& name) override {
FAIL() << name << " is not implemented";
}
};
TEST_F(FIDL_ProfileServerTest, ErrorOnInvalidDefinition) {
fidl::InterfaceHandle<fuchsia::bluetooth::bredr::ConnectionReceiver> receiver_handle;
auto request = receiver_handle.NewRequest();
std::vector<fidlbredr::ServiceDefinition> services;
fidlbredr::ServiceDefinition def;
// Empty service definition is not allowed - it must contain at least a serivce UUID.
services.emplace_back(std::move(def));
client()->Advertise(std::move(services), fidlbredr::SecurityRequirements(),
fidlbredr::ChannelParameters(), std::move(receiver_handle));
RunLoopUntilIdle();
// Server should close because it's not a good definition.
zx_signals_t signals;
request.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time(0), &signals);
EXPECT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED);
}
class FIDL_ProfileServerTest_ConnectedPeer : public FIDL_ProfileServerTest {
public:
FIDL_ProfileServerTest_ConnectedPeer() = default;
~FIDL_ProfileServerTest_ConnectedPeer() override = default;
protected:
void SetUp() override {
FIDL_ProfileServerTest::SetUp();
peer_ = peer_cache()->NewPeer(kTestDevAddr, true);
auto fake_peer = std::make_unique<bt::testing::FakePeer>(kTestDevAddr);
test_device()->AddPeer(std::move(fake_peer));
bt::testing::FakeController::Settings settings;
settings.ApplyDualModeDefaults();
test_device()->set_settings(settings);
bt::hci::Status status(bt::HostError::kFailed);
auto connect_cb = [this, &status](auto cb_status, auto cb_conn_ref) {
ASSERT_TRUE(cb_conn_ref);
status = cb_status;
connection_ = std::move(cb_conn_ref);
};
EXPECT_TRUE(conn_mgr()->Connect(peer_->identifier(), connect_cb));
EXPECT_EQ(bt::gap::Peer::ConnectionState::kInitializing, peer_->bredr()->connection_state());
RunLoopUntilIdle();
EXPECT_TRUE(status);
ASSERT_TRUE(connection_);
EXPECT_EQ(peer_->identifier(), connection_->peer_id());
EXPECT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer_->bredr()->connection_state());
}
void TearDown() override {
connection_ = nullptr;
peer_ = nullptr;
FIDL_ProfileServerTest::TearDown();
}
bt::gap::BrEdrConnection* connection() const { return connection_; }
bt::gap::Peer* peer() const { return peer_; }
private:
bt::gap::BrEdrConnection* connection_;
bt::gap::Peer* peer_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FIDL_ProfileServerTest_ConnectedPeer);
};
TEST_F(FIDL_ProfileServerTest_ConnectedPeer, ConnectL2capChannelParameters) {
auto pairing_delegate =
std::make_unique<bt::gap::FakePairingDelegate>(bt::sm::IOCapability::kDisplayYesNo);
conn_mgr()->SetPairingDelegate(pairing_delegate->GetWeakPtr());
// Approve pairing requests.
pairing_delegate->SetConfirmPairingCallback(
[](bt::PeerId, auto confirm_cb) { confirm_cb(true); });
pairing_delegate->SetCompletePairingCallback(
[&](bt::PeerId, bt::sm::Status status) { EXPECT_TRUE(status.is_success()); });
bt::l2cap::ChannelParameters expected_params;
expected_params.mode = bt::l2cap::ChannelMode::kEnhancedRetransmission;
expected_params.max_rx_sdu_size = bt::l2cap::kMinACLMTU;
data_domain()->ExpectOutboundL2capChannel(connection()->link().handle(), kPSM, 0x40, 0x41,
expected_params);
fidlbredr::ChannelParameters fidl_params;
fidl_params.set_channel_mode(fidlbredr::ChannelMode::ENHANCED_RETRANSMISSION);
fidl_params.set_max_rx_sdu_size(bt::l2cap::kMinACLMTU);
// Expect a non-empty channel result.
auto sock_cb = [](auto result) {
EXPECT_TRUE(result.is_response());
EXPECT_TRUE(!result.response().ResultValue_().IsEmpty());
};
// Initiates pairing
fuchsia::bluetooth::PeerId peer_id{peer()->identifier().value()};
client()->Connect(peer_id, kPSM, std::move(fidl_params), std::move(sock_cb));
RunLoopUntilIdle();
}
// Tests receiving an empty Channel results in an error propagated through the callback.
TEST_F(FIDL_ProfileServerTest_ConnectedPeer, ConnectEmptyChannelResponse) {
auto pairing_delegate =
std::make_unique<bt::gap::FakePairingDelegate>(bt::sm::IOCapability::kDisplayYesNo);
conn_mgr()->SetPairingDelegate(pairing_delegate->GetWeakPtr());
// Approve pairing requests.
pairing_delegate->SetConfirmPairingCallback(
[](bt::PeerId, auto confirm_cb) { confirm_cb(true); });
pairing_delegate->SetCompletePairingCallback(
[&](bt::PeerId, bt::sm::Status status) { EXPECT_TRUE(status.is_success()); });
// Make the l2cap channel creation fail.
data_domain()->set_simulate_open_channel_failure(true);
bt::l2cap::ChannelParameters expected_params;
expected_params.mode = bt::l2cap::ChannelMode::kEnhancedRetransmission;
expected_params.max_rx_sdu_size = bt::l2cap::kMinACLMTU;
data_domain()->ExpectOutboundL2capChannel(connection()->link().handle(), kPSM, 0x40, 0x41,
expected_params);
fidlbredr::ChannelParameters fidl_params;
fidl_params.set_channel_mode(fidlbredr::ChannelMode::ENHANCED_RETRANSMISSION);
fidl_params.set_max_rx_sdu_size(bt::l2cap::kMinACLMTU);
auto sock_cb = [](auto result) {
EXPECT_TRUE(result.is_err());
EXPECT_EQ(fuchsia::bluetooth::ErrorCode::FAILED, result.err());
};
// Initiates pairing
fuchsia::bluetooth::PeerId peer_id{peer()->identifier().value()};
client()->Connect(peer_id, kPSM, std::move(fidl_params), std::move(sock_cb));
RunLoopUntilIdle();
}
TEST_F(FIDL_ProfileServerTest_ConnectedPeer,
AddServiceChannelParametersReceivedInOnChannelConnectedCallback) {
fidlbredr::ChannelParameters fidl_chan_params;
fidl_chan_params.set_channel_mode(fidlbredr::ChannelMode::ENHANCED_RETRANSMISSION);
constexpr uint16_t kTxMtu = bt::l2cap::kMinACLMTU;
auto pairing_delegate =
std::make_unique<bt::gap::FakePairingDelegate>(bt::sm::IOCapability::kDisplayYesNo);
conn_mgr()->SetPairingDelegate(pairing_delegate->GetWeakPtr());
using ::testing::StrictMock;
fidl::InterfaceHandle<fidlbredr::ConnectionReceiver> connect_receiver_handle;
auto connect_receiver = std::make_unique<StrictMock<MockConnectionReceiver>>(
connect_receiver_handle.NewRequest(), dispatcher());
std::vector<fidlbredr::ServiceDefinition> services;
services.emplace_back(MakeFIDLServiceDefinition());
client()->Advertise(std::move(services), fidlbredr::SecurityRequirements(),
std::move(fidl_chan_params), std::move(connect_receiver_handle));
RunLoopUntilIdle();
EXPECT_CALL(*connect_receiver, Connected(::testing::_, ::testing::_, ::testing::_))
.WillOnce([expected_id = peer()->identifier()](fuchsia::bluetooth::PeerId peer_id,
fidlbredr::Channel channel,
::testing::Unused) {
ASSERT_EQ(expected_id.value(), peer_id.value);
ASSERT_TRUE(channel.has_socket());
});
EXPECT_TRUE(data_domain()->TriggerInboundL2capChannel(connection()->link().handle(), kPSM, 0x40,
0x41, kTxMtu));
RunLoopUntilIdle();
}
} // namespace
} // namespace bthost