blob: 965f979e7ffb5f7aea8c10fd72a78112a9935487 [file] [log] [blame]
// Copyright 2017 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_channel.h"
#include <lib/async/cpp/task.h>
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/run_or_post.h"
namespace bt::l2cap::testing {
FakeChannel::FakeChannel(ChannelId id, ChannelId remote_id, hci::ConnectionHandle handle,
hci::Connection::LinkType link_type, ChannelInfo info)
: Channel(id, remote_id, link_type, handle, info),
handle_(handle),
fragmenter_(handle),
send_dispatcher_(nullptr),
activate_fails_(false),
link_error_(false),
acl_priority_fails_(false),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(handle_);
}
void FakeChannel::Receive(const ByteBuffer& data) {
auto pdu = fragmenter_.BuildFrame(id(), data, FrameCheckSequenceOption::kNoFcs);
auto sdu = std::make_unique<DynamicByteBuffer>(pdu.length());
pdu.Copy(sdu.get());
if (rx_cb_) {
rx_cb_(std::move(sdu));
} else {
pending_rx_sdus_.push(std::move(sdu));
}
}
void FakeChannel::SetSendCallback(SendCallback callback, async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(static_cast<bool>(callback) == static_cast<bool>(dispatcher));
send_cb_ = std::move(callback);
send_dispatcher_ = dispatcher;
}
void FakeChannel::SetLinkErrorCallback(LinkErrorCallback callback) {
link_err_cb_ = std::move(callback);
}
void FakeChannel::SetSecurityCallback(SecurityUpgradeCallback callback,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(static_cast<bool>(callback) == static_cast<bool>(dispatcher));
security_cb_ = std::move(callback);
security_dispatcher_ = dispatcher;
}
void FakeChannel::Close() {
if (closed_cb_)
closed_cb_();
}
bool FakeChannel::Activate(RxCallback rx_callback, ClosedCallback closed_callback) {
ZX_DEBUG_ASSERT(rx_callback);
ZX_DEBUG_ASSERT(closed_callback);
ZX_DEBUG_ASSERT(!rx_cb_);
ZX_DEBUG_ASSERT(!closed_cb_);
if (activate_fails_)
return false;
closed_cb_ = std::move(closed_callback);
rx_cb_ = std::move(rx_callback);
while (!pending_rx_sdus_.empty()) {
rx_cb_(std::move(pending_rx_sdus_.front()));
pending_rx_sdus_.pop();
}
return true;
}
void FakeChannel::Deactivate() {
closed_cb_ = {};
rx_cb_ = {};
}
void FakeChannel::SignalLinkError() {
link_error_ = true;
if (link_err_cb_) {
link_err_cb_();
}
}
bool FakeChannel::Send(ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
if (!send_cb_)
return false;
if (sdu->size() > max_tx_sdu_size()) {
bt_log(ERROR, "l2cap", "Dropping oversized SDU (sdu->size()=%zu, max_tx_sdu_size()=%u)",
sdu->size(), max_tx_sdu_size());
return false;
}
ZX_DEBUG_ASSERT(send_dispatcher_);
async::PostTask(send_dispatcher_,
[cb = send_cb_.share(), sdu = std::move(sdu)]() mutable { cb(std::move(sdu)); });
return true;
}
void FakeChannel::UpgradeSecurity(sm::SecurityLevel level, sm::StatusCallback callback,
async_dispatcher_t* dispatcher) {
ZX_ASSERT(dispatcher);
auto result_cb = [cb = std::move(callback), dispatcher](auto status) mutable {
async::PostTask(dispatcher, [cb = std::move(cb), status] { cb(status); });
};
ZX_ASSERT(security_dispatcher_);
async::PostTask(security_dispatcher_,
[cb = std::move(result_cb), f = security_cb_.share(), handle = handle_,
level]() mutable { f(handle, level, std::move(cb)); });
}
void FakeChannel::RequestAclPriority(hci::AclPriority priority,
fit::callback<void(fit::result<>)> cb) {
if (acl_priority_fails_) {
cb(fit::error());
return;
}
requested_acl_priority_ = priority;
cb(fit::ok());
}
void FakeChannel::SetBrEdrAutomaticFlushTimeout(
zx::duration flush_timeout, fit::callback<void(fit::result<void, hci::StatusCode>)> callback) {
if (!flush_timeout_succeeds_) {
callback(fit::error(hci::StatusCode::kUnspecifiedError));
return;
}
info_.flush_timeout = flush_timeout;
callback(fit::ok());
}
} // namespace bt::l2cap::testing