blob: 9c2a3e35db04a681d840bd59daea5bc64d792ae8 [file] [log] [blame]
// Copyright 2018 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 "fake_domain.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_channel.h"
namespace bt {
using l2cap::testing::FakeChannel;
namespace data {
namespace testing {
void FakeDomain::TriggerLEConnectionParameterUpdate(
hci::ConnectionHandle handle,
const hci::LEPreferredConnectionParameters& params) {
ZX_DEBUG_ASSERT(initialized_);
LinkData& link_data = ConnectedLinkData(handle);
async::PostTask(
link_data.dispatcher,
[params, cb = link_data.le_conn_param_cb.share()] { cb(params); });
}
void FakeDomain::ExpectOutboundL2capChannel(hci::ConnectionHandle handle,
l2cap::PSM psm, l2cap::ChannelId id,
l2cap::ChannelId remote_id) {
ZX_DEBUG_ASSERT(initialized_);
LinkData& link_data = GetLinkData(handle);
link_data.expected_outbound_conns[psm].emplace(id, remote_id);
}
void FakeDomain::TriggerInboundL2capChannel(hci::ConnectionHandle handle,
l2cap::PSM psm, l2cap::ChannelId id,
l2cap::ChannelId remote_id) {
ZX_DEBUG_ASSERT(initialized_);
LinkData& link_data = ConnectedLinkData(handle);
auto cb_iter = inbound_conn_cbs_.find(psm);
ZX_DEBUG_ASSERT_MSG(cb_iter != inbound_conn_cbs_.end(),
"no service registered for PSM %#.4x", psm);
l2cap::ChannelCallback& cb = cb_iter->second.first;
async_dispatcher_t* const dispatcher = cb_iter->second.second;
auto chan = OpenFakeChannel(&link_data, id, remote_id);
async::PostTask(dispatcher, [cb = cb.share(), chan = std::move(chan)] {
cb(std::move(chan));
});
}
void FakeDomain::TriggerLinkError(hci::ConnectionHandle handle) {
ZX_DEBUG_ASSERT(initialized_);
LinkData& link_data = ConnectedLinkData(handle);
async::PostTask(link_data.dispatcher,
[cb = link_data.link_error_cb.share()] { cb(); });
}
void FakeDomain::Initialize() { initialized_ = true; }
void FakeDomain::ShutDown() { initialized_ = false; }
void FakeDomain::AddACLConnection(hci::ConnectionHandle handle,
hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_cb,
l2cap::SecurityUpgradeCallback security_cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
RegisterInternal(handle, role, hci::Connection::LinkType::kACL,
std::move(link_error_cb), dispatcher);
}
void FakeDomain::AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_cb,
l2cap::LEConnectionParameterUpdateCallback conn_param_cb,
l2cap::LEFixedChannelsCallback channel_cb,
l2cap::SecurityUpgradeCallback security_cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
LinkData* data =
RegisterInternal(handle, role, hci::Connection::LinkType::kLE,
std::move(link_error_cb), dispatcher);
data->le_conn_param_cb = std::move(conn_param_cb);
// Open the ATT and SMP fixed channels.
auto att = OpenFakeFixedChannel(data, l2cap::kATTChannelId);
auto smp = OpenFakeFixedChannel(data, l2cap::kLESMPChannelId);
async::PostTask(dispatcher, [att = std::move(att), smp = std::move(smp),
cb = std::move(channel_cb)]() mutable {
cb(std::move(att), std::move(smp));
});
}
void FakeDomain::RemoveConnection(hci::ConnectionHandle handle) {
links_.erase(handle);
}
void FakeDomain::AssignLinkSecurityProperties(hci::ConnectionHandle handle,
sm::SecurityProperties security) {
// TODO(armansito): implement
}
void FakeDomain::OpenL2capChannel(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelCallback cb,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(initialized_);
LinkData& link_data = ConnectedLinkData(handle);
auto psm_it = link_data.expected_outbound_conns.find(psm);
ZX_DEBUG_ASSERT_MSG(psm_it != link_data.expected_outbound_conns.end() &&
!psm_it->second.empty(),
"Unexpected outgoing L2CAP connection (PSM %#.4x)", psm);
auto [id, remote_id] = psm_it->second.front();
psm_it->second.pop();
auto chan = OpenFakeChannel(&link_data, id, remote_id);
async::PostTask(dispatcher, [cb = std::move(cb), chan = std::move(chan)]() {
cb(std::move(chan));
});
}
void FakeDomain::OpenL2capChannel(hci::ConnectionHandle handle, l2cap::PSM psm,
SocketCallback socket_callback,
async_dispatcher_t* cb_dispatcher) {
ZX_DEBUG_ASSERT(cb_dispatcher);
OpenL2capChannel(
handle, psm,
[this, cb = std::move(socket_callback),
cb_dispatcher](auto channel) mutable {
zx::socket s = socket_factory_.MakeSocketForChannel(channel);
// Called every time the service is connected, cb must be shared.
async::PostTask(cb_dispatcher,
[s = std::move(s), cb = cb.share(),
handle = channel->link_handle()]() mutable {
cb(std::move(s), handle);
});
},
async_get_default_dispatcher());
}
void FakeDomain::RegisterService(l2cap::PSM psm,
l2cap::ChannelCallback channel_callback,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(initialized_);
ZX_DEBUG_ASSERT(inbound_conn_cbs_.count(psm) == 0);
inbound_conn_cbs_.emplace(
psm, std::make_pair(std::move(channel_callback), dispatcher));
}
void FakeDomain::RegisterService(l2cap::PSM psm, SocketCallback socket_callback,
async_dispatcher_t* cb_dispatcher) {
RegisterService(
psm,
[this, psm, cb = std::move(socket_callback),
cb_dispatcher](auto channel) mutable {
zx::socket s = socket_factory_.MakeSocketForChannel(channel);
// Called every time the service is connected, cb must be shared.
async::PostTask(cb_dispatcher,
[s = std::move(s), cb = cb.share(),
handle = channel->link_handle()]() mutable {
cb(std::move(s), handle);
});
},
async_get_default_dispatcher());
}
void FakeDomain::UnregisterService(l2cap::PSM psm) {
ZX_DEBUG_ASSERT(initialized_);
inbound_conn_cbs_.erase(psm);
}
FakeDomain::~FakeDomain() {
for (auto& link_it : links_) {
for (auto& psm_it : link_it.second.expected_outbound_conns) {
ZX_DEBUG_ASSERT_MSG(psm_it.second.empty(),
"didn't receive expected connection on PSM %#.4x",
psm_it.first);
}
}
}
FakeDomain::LinkData* FakeDomain::RegisterInternal(
hci::ConnectionHandle handle, hci::Connection::Role role,
hci::Connection::LinkType link_type, l2cap::LinkErrorCallback link_error_cb,
async_dispatcher_t* dispatcher) {
auto& data = GetLinkData(handle);
ZX_DEBUG_ASSERT_MSG(!data.connected,
"connection handle re-used (handle: %#.4x)", handle);
data.connected = true;
data.role = role;
data.type = link_type;
data.link_error_cb = std::move(link_error_cb);
data.dispatcher = dispatcher;
return &data;
}
fbl::RefPtr<FakeChannel> FakeDomain::OpenFakeChannel(
LinkData* link, l2cap::ChannelId id, l2cap::ChannelId remote_id) {
auto chan =
fbl::AdoptRef(new FakeChannel(id, remote_id, link->handle, link->type));
chan->SetLinkErrorCallback(link->link_error_cb.share(), link->dispatcher);
if (chan_cb_) {
chan_cb_(chan);
}
return chan;
}
fbl::RefPtr<FakeChannel> FakeDomain::OpenFakeFixedChannel(LinkData* link,
l2cap::ChannelId id) {
return OpenFakeChannel(link, id, id);
}
FakeDomain::LinkData& FakeDomain::GetLinkData(hci::ConnectionHandle handle) {
auto [it, inserted] = links_.try_emplace(handle);
auto& data = it->second;
if (inserted) {
data.connected = false;
data.handle = handle;
}
return data;
}
FakeDomain::LinkData& FakeDomain::ConnectedLinkData(
hci::ConnectionHandle handle) {
auto link_iter = links_.find(handle);
ZX_DEBUG_ASSERT_MSG(link_iter != links_.end(),
"fake link not found (handle: %#.4x)", handle);
ZX_DEBUG_ASSERT_MSG(link_iter->second.connected,
"fake link not connected yet (handle: %#.4x)", handle);
return link_iter->second;
}
} // namespace testing
} // namespace data
} // namespace bt