blob: f09eb823c8352aed3c0e54d34f0214e0fbb060f0 [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/fake_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/host_error.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
namespace bt::l2cap::testing {
FakeChannel::FakeChannel(ChannelId id,
ChannelId remote_id,
hci_spec::ConnectionHandle handle,
bt::LinkType link_type,
ChannelInfo info,
uint16_t max_tx_queued)
: Channel(id, remote_id, link_type, handle, info, max_tx_queued),
handle_(handle),
fragmenter_(handle),
activate_fails_(false),
link_error_(false),
acl_priority_fails_(false),
weak_fake_chan_(this) {}
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) {
send_cb_ = std::move(callback);
}
void FakeChannel::SetSendCallback(SendCallback callback,
pw::async::Dispatcher& dispatcher) {
SetSendCallback(std::move(callback));
send_dispatcher_.emplace(dispatcher);
}
void FakeChannel::SetLinkErrorCallback(LinkErrorCallback callback) {
link_err_cb_ = std::move(callback);
}
void FakeChannel::SetSecurityCallback(SecurityUpgradeCallback callback,
pw::async::Dispatcher& dispatcher) {
security_cb_ = std::move(callback);
security_dispatcher_.emplace(dispatcher);
}
void FakeChannel::Close() {
if (closed_cb_)
closed_cb_();
}
bool FakeChannel::Activate(RxCallback rx_callback,
ClosedCallback closed_callback) {
BT_DEBUG_ASSERT(rx_callback);
BT_DEBUG_ASSERT(closed_callback);
BT_DEBUG_ASSERT(!rx_cb_);
BT_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() {
if (link_error_) {
return;
}
link_error_ = true;
if (link_err_cb_) {
link_err_cb_();
}
}
bool FakeChannel::Send(ByteBufferPtr sdu) {
BT_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;
}
if (send_dispatcher_) {
(void)send_dispatcher_->Post(
[cb = send_cb_.share(), sdu = std::move(sdu)](
pw::async::Context /*ctx*/, pw::Status status) mutable {
if (status.ok()) {
cb(std::move(sdu));
}
});
} else {
send_cb_(std::move(sdu));
}
return true;
}
void FakeChannel::UpgradeSecurity(sm::SecurityLevel level,
sm::ResultFunction<> callback) {
BT_ASSERT(security_dispatcher_);
(void)security_dispatcher_->Post(
[cb = std::move(callback),
f = security_cb_.share(),
handle = handle_,
level](pw::async::Context /*ctx*/, pw::Status status) mutable {
if (status.ok()) {
f(handle, level, std::move(cb));
}
});
}
void FakeChannel::RequestAclPriority(
pw::bluetooth::AclPriority priority,
fit::callback<void(fit::result<fit::failed>)> cb) {
if (acl_priority_fails_) {
cb(fit::failed());
return;
}
requested_acl_priority_ = priority;
cb(fit::ok());
}
void FakeChannel::SetBrEdrAutomaticFlushTimeout(
pw::chrono::SystemClock::duration flush_timeout,
hci::ResultCallback<> callback) {
if (!flush_timeout_succeeds_) {
callback(ToResult(pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR));
return;
}
info_.flush_timeout = flush_timeout;
callback(fit::ok());
}
void FakeChannel::StartA2dpOffload(
const A2dpOffloadManager::Configuration& config,
hci::ResultCallback<> callback) {
if (a2dp_offload_error_.has_value()) {
callback(ToResult(a2dp_offload_error_.value()));
return;
}
callback(fit::ok());
}
void FakeChannel::StopA2dpOffload(hci::ResultCallback<> callback) {
if (a2dp_offload_error_.has_value()) {
callback(ToResult(a2dp_offload_error_.value()));
return;
}
callback(fit::ok());
}
} // namespace bt::l2cap::testing