blob: ed0fdcd6d3612386502e9a6b910f1abb9f083b33 [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 "emulated_peer.h"
#include <fuchsia/bluetooth/test/cpp/fidl.h>
namespace fbt = fuchsia::bluetooth;
namespace ftest = fuchsia::bluetooth::test;
namespace bt_hci_virtual {
namespace {
bt::DeviceAddress::Type LeAddressTypeFromFidl(fbt::AddressType type) {
return (type == fbt::AddressType::RANDOM) ? bt::DeviceAddress::Type::kLERandom
: bt::DeviceAddress::Type::kLEPublic;
}
bt::DeviceAddress LeAddressFromFidl(const fbt::Address& address) {
return bt::DeviceAddress(LeAddressTypeFromFidl(address.type), address.bytes);
}
pw::bluetooth::emboss::ConnectionRole ConnectionRoleFromFidl(fbt::ConnectionRole role) {
switch (role) {
case fbt::ConnectionRole::LEADER:
return pw::bluetooth::emboss::ConnectionRole::CENTRAL;
case fbt::ConnectionRole::FOLLOWER:
[[fallthrough]];
default:
break;
}
return pw::bluetooth::emboss::ConnectionRole::PERIPHERAL;
}
} // namespace
// static
EmulatedPeer::Result EmulatedPeer::NewLowEnergy(
ftest::LowEnergyPeerParameters parameters,
fidl::InterfaceRequest<fuchsia::bluetooth::test::Peer> request,
bt::testing::FakeController* fake_controller) {
ZX_DEBUG_ASSERT(request);
ZX_DEBUG_ASSERT(fake_controller);
if (!parameters.has_address()) {
bt_log(ERROR, "virtual", "A fake peer address is mandatory!\n");
return fpromise::error(ftest::EmulatorPeerError::PARAMETERS_INVALID);
}
bt::BufferView adv, scan_response;
if (parameters.has_advertisement()) {
adv = bt::BufferView(parameters.advertisement().data);
}
if (parameters.has_scan_response()) {
scan_response = bt::BufferView(parameters.scan_response().data);
}
auto address = LeAddressFromFidl(parameters.address());
bool connectable = parameters.has_connectable() && parameters.connectable();
bool scannable = scan_response.size() != 0u;
// TODO(armansito): We should consider splitting bt::testing::FakePeer into separate types for
// BR/EDR and LE transport emulation logic.
auto peer = std::make_unique<bt::testing::FakePeer>(address, fake_controller->pw_dispatcher(),
connectable, scannable);
peer->set_advertising_data(adv);
if (scannable) {
peer->set_scan_response(scan_response);
}
if (!fake_controller->AddPeer(std::move(peer))) {
bt_log(ERROR, "virtual", "A fake LE peer with given address already exists: %s\n",
address.ToString().c_str());
return fpromise::error(ftest::EmulatorPeerError::ADDRESS_REPEATED);
}
return fpromise::ok(std::unique_ptr<EmulatedPeer>(
new EmulatedPeer(address, std::move(request), fake_controller)));
}
// static
EmulatedPeer::Result EmulatedPeer::NewBredr(
ftest::BredrPeerParameters parameters,
fidl::InterfaceRequest<fuchsia::bluetooth::test::Peer> request,
bt::testing::FakeController* fake_controller) {
ZX_DEBUG_ASSERT(request);
ZX_DEBUG_ASSERT(fake_controller);
if (!parameters.has_address()) {
bt_log(ERROR, "virtual", "A fake peer address is mandatory!\n");
return fpromise::error(ftest::EmulatorPeerError::PARAMETERS_INVALID);
}
auto address = bt::DeviceAddress(bt::DeviceAddress::Type::kBREDR, parameters.address().bytes);
bool connectable = parameters.has_connectable() && parameters.connectable();
// TODO(armansito): We should consider splitting bt::testing::FakePeer into separate types for
// BR/EDR and LE transport emulation logic.
auto peer = std::make_unique<bt::testing::FakePeer>(address, fake_controller->pw_dispatcher(),
connectable, false);
if (parameters.has_device_class()) {
peer->set_class_of_device(bt::DeviceClass(parameters.device_class().value));
}
if (parameters.has_service_definition()) {
std::vector<bt::sdp::ServiceRecord> recs;
for (const auto& defn : parameters.service_definition()) {
auto rec = bthost::fidl_helpers::ServiceDefinitionToServiceRecord(defn);
if (rec.is_ok()) {
recs.emplace_back(std::move(rec.value()));
}
}
bt::l2cap::ChannelParameters params;
auto NopConnectCallback = [](auto /*channel*/, const bt::sdp::DataElement&) {};
peer->sdp_server()->server()->RegisterService(std::move(recs), params, NopConnectCallback);
}
if (!fake_controller->AddPeer(std::move(peer))) {
bt_log(ERROR, "virtual", "A fake BR/EDR peer with given address already exists: %s\n",
address.ToString().c_str());
return fpromise::error(ftest::EmulatorPeerError::ADDRESS_REPEATED);
}
return fpromise::ok(std::unique_ptr<EmulatedPeer>(
new EmulatedPeer(address, std::move(request), fake_controller)));
}
EmulatedPeer::EmulatedPeer(bt::DeviceAddress address, fidl::InterfaceRequest<ftest::Peer> request,
bt::testing::FakeController* fake_controller)
: address_(address), fake_controller_(fake_controller), binding_(this, std::move(request)) {
ZX_DEBUG_ASSERT(fake_controller_);
ZX_DEBUG_ASSERT(binding_.is_bound());
binding_.set_error_handler(fit::bind_member<&EmulatedPeer::OnChannelClosed>(this));
}
EmulatedPeer::~EmulatedPeer() { CleanUp(); }
void EmulatedPeer::AssignConnectionStatus(ftest::HciError status,
AssignConnectionStatusCallback callback) {
bt_log(TRACE, "virtual", "EmulatedPeer.AssignConnectionStatus\n");
auto peer = fake_controller_->FindPeer(address_);
if (peer) {
peer->set_connect_response(static_cast<pw::bluetooth::emboss::StatusCode>(status));
}
callback();
}
void EmulatedPeer::EmulateLeConnectionComplete(fbt::ConnectionRole role) {
bt_log(TRACE, "virtual", "EmulatedPeer.EmulateLeConnectionComplete\n");
fake_controller_->ConnectLowEnergy(address_, ConnectionRoleFromFidl(role));
}
void EmulatedPeer::EmulateDisconnectionComplete() {
bt_log(TRACE, "virtual", "EmulatedPeer.EmulateDisconnectionComplete\n");
fake_controller_->Disconnect(address_);
}
void EmulatedPeer::WatchConnectionStates(WatchConnectionStatesCallback callback) {
bt_log(TRACE, "virtual", "EmulatedPeer.WatchConnectionState\n");
connection_state_getter_.Watch(std::move(callback));
}
void EmulatedPeer::UpdateConnectionState(bool connected) {
connection_state_getter_.Add(connected ? ftest::ConnectionState::CONNECTED
: ftest::ConnectionState::DISCONNECTED);
}
void EmulatedPeer::OnChannelClosed(zx_status_t status) {
bt_log(TRACE, "virtual", "EmulatedPeer channel closed\n");
NotifyChannelClosed();
}
void EmulatedPeer::CleanUp() { fake_controller_->RemovePeer(address_); }
void EmulatedPeer::NotifyChannelClosed() {
if (closed_callback_) {
closed_callback_();
}
}
} // namespace bt_hci_virtual