blob: 809bd0ec0c884de21b390ad5f8acf6bce6c17121 [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_layer.h"
#include <lib/async/cpp/task.h>
#include "fake_channel.h"
namespace btlib {
namespace l2cap {
namespace testing {
void FakeLayer::TriggerLEConnectionParameterUpdate(
hci::ConnectionHandle handle,
const hci::LEPreferredConnectionParameters& params) {
FXL_DCHECK(initialized_);
LinkData& link_data = FindLinkData(handle);
async::PostTask(
link_data.dispatcher,
[params, cb = link_data.le_conn_param_cb.share()] { cb(params); });
}
void FakeLayer::TriggerOutboundChannel(hci::ConnectionHandle handle, PSM psm,
ChannelId id, ChannelId remote_id) {
FXL_DCHECK(initialized_);
LinkData& link_data = FindLinkData(handle);
auto cb_iter = link_data.outbound_conn_cbs.find(psm);
FXL_DCHECK(cb_iter != link_data.outbound_conn_cbs.end())
<< "l2cap: no previous call to OpenChannel with PSM " << psm;
std::list<ChannelDelivery>& handlers = cb_iter->second;
FXL_DCHECK(!handlers.empty());
ChannelCallback& cb = handlers.front().first;
auto chan = OpenFakeChannel(&link_data, id, remote_id);
async_dispatcher_t* const dispatcher = handlers.front().second;
async::PostTask(dispatcher, [cb = std::move(cb), chan = std::move(chan)] {
cb(std::move(chan));
});
handlers.pop_front();
if (handlers.empty()) {
link_data.outbound_conn_cbs.erase(cb_iter);
}
}
void FakeLayer::TriggerInboundChannel(hci::ConnectionHandle handle, PSM psm,
ChannelId id, ChannelId remote_id) {
FXL_DCHECK(initialized_);
LinkData& link_data = FindLinkData(handle);
auto cb_iter = inbound_conn_cbs_.find(psm);
FXL_DCHECK(cb_iter != inbound_conn_cbs_.end())
<< "l2cap: no service registered for PSM " << psm;
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 = std::move(cb), chan = std::move(chan)] {
cb(std::move(chan));
});
}
void FakeLayer::Initialize() { initialized_ = true; }
void FakeLayer::ShutDown() { initialized_ = false; }
void FakeLayer::AddACLConnection(hci::ConnectionHandle handle,
hci::Connection::Role role,
LinkErrorCallback link_error_cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
RegisterInternal(handle, role, hci::Connection::LinkType::kACL,
std::move(link_error_cb), dispatcher);
}
void FakeLayer::AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
LEConnectionParameterUpdateCallback conn_param_cb,
LinkErrorCallback link_error_cb, AddLEConnectionCallback channel_callback,
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, kATTChannelId);
auto smp = OpenFakeFixedChannel(data, kLESMPChannelId);
async::PostTask(dispatcher, [att = std::move(att), smp = std::move(smp),
cb = std::move(channel_callback)]() mutable {
cb(std::move(att), std::move(smp));
});
}
void FakeLayer::RemoveConnection(hci::ConnectionHandle handle) {
links_.erase(handle);
}
void FakeLayer::OpenChannel(hci::ConnectionHandle handle, PSM psm,
ChannelCallback cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
LinkData& link_data = FindLinkData(handle);
link_data.outbound_conn_cbs[psm].push_back(
std::make_pair(std::move(cb), dispatcher));
}
bool FakeLayer::RegisterService(PSM psm, ChannelCallback cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return false;
auto result =
inbound_conn_cbs_.emplace(psm, std::make_pair(std::move(cb), dispatcher));
return result.second;
}
void FakeLayer::UnregisterService(PSM psm) {
if (!initialized_)
return;
inbound_conn_cbs_.erase(psm);
}
FakeLayer::LinkData* FakeLayer::RegisterInternal(
hci::ConnectionHandle handle, hci::Connection::Role role,
hci::Connection::LinkType link_type, LinkErrorCallback link_error_cb,
async_dispatcher_t* dispatcher) {
FXL_DCHECK(links_.find(handle) == links_.end())
<< "l2cap: Connection handle re-used!";
LinkData data;
data.handle = handle;
data.role = role;
data.type = link_type;
data.link_error_cb = std::move(link_error_cb);
data.dispatcher = dispatcher;
auto insert_res = links_.emplace(handle, std::move(data));
return &insert_res.first->second;
}
fbl::RefPtr<FakeChannel> FakeLayer::OpenFakeChannel(LinkData* link,
ChannelId id,
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> FakeLayer::OpenFakeFixedChannel(LinkData* link,
ChannelId id) {
return OpenFakeChannel(link, id, id);
}
FakeLayer::LinkData& FakeLayer::FindLinkData(hci::ConnectionHandle handle) {
auto link_iter = links_.find(handle);
FXL_DCHECK(link_iter != links_.end())
<< "l2cap: fake link not found: (handle: " << handle << ")";
return link_iter->second;
}
} // namespace testing
} // namespace l2cap
} // namespace btlib