1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
20
21 namespace bt::l2cap::testing {
22
FakeChannel(ChannelId id,ChannelId remote_id,hci_spec::ConnectionHandle handle,bt::LinkType link_type,ChannelInfo info,uint16_t max_tx_queued)23 FakeChannel::FakeChannel(ChannelId id,
24 ChannelId remote_id,
25 hci_spec::ConnectionHandle handle,
26 bt::LinkType link_type,
27 ChannelInfo info,
28 uint16_t max_tx_queued)
29 : Channel(id, remote_id, link_type, handle, info, max_tx_queued),
30 handle_(handle),
31 fragmenter_(handle),
32 activate_fails_(false),
33 link_error_(false),
34 acl_priority_fails_(false),
35 weak_fake_chan_(this) {}
36
Receive(const ByteBuffer & data)37 void FakeChannel::Receive(const ByteBuffer& data) {
38 auto pdu =
39 fragmenter_.BuildFrame(id(), data, FrameCheckSequenceOption::kNoFcs);
40 auto sdu = std::make_unique<DynamicByteBuffer>(pdu.length());
41 pdu.Copy(sdu.get());
42 if (rx_cb_) {
43 rx_cb_(std::move(sdu));
44 } else {
45 pending_rx_sdus_.push(std::move(sdu));
46 }
47 }
48
SetSendCallback(SendCallback callback)49 void FakeChannel::SetSendCallback(SendCallback callback) {
50 send_cb_ = std::move(callback);
51 }
52
SetSendCallback(SendCallback callback,pw::async::Dispatcher & dispatcher)53 void FakeChannel::SetSendCallback(SendCallback callback,
54 pw::async::Dispatcher& dispatcher) {
55 SetSendCallback(std::move(callback));
56 send_dispatcher_.emplace(dispatcher);
57 }
58
SetLinkErrorCallback(LinkErrorCallback callback)59 void FakeChannel::SetLinkErrorCallback(LinkErrorCallback callback) {
60 link_err_cb_ = std::move(callback);
61 }
62
SetSecurityCallback(SecurityUpgradeCallback callback,pw::async::Dispatcher & dispatcher)63 void FakeChannel::SetSecurityCallback(SecurityUpgradeCallback callback,
64 pw::async::Dispatcher& dispatcher) {
65 security_cb_ = std::move(callback);
66 security_dispatcher_.emplace(dispatcher);
67 }
68
Close()69 void FakeChannel::Close() {
70 if (closed_cb_)
71 closed_cb_();
72 }
73
Activate(RxCallback rx_callback,ClosedCallback closed_callback)74 bool FakeChannel::Activate(RxCallback rx_callback,
75 ClosedCallback closed_callback) {
76 PW_DCHECK(rx_callback);
77 PW_DCHECK(closed_callback);
78 PW_DCHECK(!rx_cb_);
79 PW_DCHECK(!closed_cb_);
80
81 if (activate_fails_)
82 return false;
83
84 closed_cb_ = std::move(closed_callback);
85 rx_cb_ = std::move(rx_callback);
86
87 while (!pending_rx_sdus_.empty()) {
88 rx_cb_(std::move(pending_rx_sdus_.front()));
89 pending_rx_sdus_.pop();
90 }
91
92 return true;
93 }
94
Deactivate()95 void FakeChannel::Deactivate() {
96 closed_cb_ = {};
97 rx_cb_ = {};
98 }
99
SignalLinkError()100 void FakeChannel::SignalLinkError() {
101 if (link_error_) {
102 return;
103 }
104 link_error_ = true;
105
106 if (link_err_cb_) {
107 link_err_cb_();
108 }
109 }
110
Send(ByteBufferPtr sdu)111 bool FakeChannel::Send(ByteBufferPtr sdu) {
112 PW_DCHECK(sdu);
113
114 if (!send_cb_)
115 return false;
116
117 if (sdu->size() > max_tx_sdu_size()) {
118 bt_log(ERROR,
119 "l2cap",
120 "Dropping oversized SDU (sdu->size()=%zu, max_tx_sdu_size()=%u)",
121 sdu->size(),
122 max_tx_sdu_size());
123 return false;
124 }
125
126 if (send_dispatcher_) {
127 (void)send_dispatcher_->Post(
128 [cb = send_cb_.share(), sdu = std::move(sdu)](
129 pw::async::Context /*ctx*/, pw::Status status) mutable {
130 if (status.ok()) {
131 cb(std::move(sdu));
132 }
133 });
134 } else {
135 send_cb_(std::move(sdu));
136 }
137
138 return true;
139 }
140
UpgradeSecurity(sm::SecurityLevel level,sm::ResultFunction<> callback)141 void FakeChannel::UpgradeSecurity(sm::SecurityLevel level,
142 sm::ResultFunction<> callback) {
143 PW_CHECK(security_dispatcher_);
144 (void)security_dispatcher_->Post(
145 [cb = std::move(callback),
146 f = security_cb_.share(),
147 handle = handle_,
148 level](pw::async::Context /*ctx*/, pw::Status status) mutable {
149 if (status.ok()) {
150 f(handle, level, std::move(cb));
151 }
152 });
153 }
154
RequestAclPriority(pw::bluetooth::AclPriority priority,fit::callback<void (fit::result<fit::failed>)> cb)155 void FakeChannel::RequestAclPriority(
156 pw::bluetooth::AclPriority priority,
157 fit::callback<void(fit::result<fit::failed>)> cb) {
158 if (acl_priority_fails_) {
159 cb(fit::failed());
160 return;
161 }
162 requested_acl_priority_ = priority;
163 cb(fit::ok());
164 }
165
SetBrEdrAutomaticFlushTimeout(pw::chrono::SystemClock::duration flush_timeout,hci::ResultCallback<> callback)166 void FakeChannel::SetBrEdrAutomaticFlushTimeout(
167 pw::chrono::SystemClock::duration flush_timeout,
168 hci::ResultCallback<> callback) {
169 if (!flush_timeout_succeeds_) {
170 callback(ToResult(pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR));
171 return;
172 }
173 info_.flush_timeout = flush_timeout;
174 callback(fit::ok());
175 }
176
StartA2dpOffload(const A2dpOffloadManager::Configuration &,hci::ResultCallback<> callback)177 void FakeChannel::StartA2dpOffload(const A2dpOffloadManager::Configuration&,
178 hci::ResultCallback<> callback) {
179 if (a2dp_offload_error_.has_value()) {
180 callback(ToResult(a2dp_offload_error_.value()));
181 audio_offloading_status_ = A2dpOffloadStatus::kStopped;
182 return;
183 }
184 audio_offloading_status_ = A2dpOffloadStatus::kStarted;
185 callback(fit::ok());
186 }
187
StopA2dpOffload(hci::ResultCallback<> callback)188 void FakeChannel::StopA2dpOffload(hci::ResultCallback<> callback) {
189 if (a2dp_offload_error_.has_value()) {
190 callback(ToResult(a2dp_offload_error_.value()));
191 return;
192 }
193 audio_offloading_status_ = A2dpOffloadStatus::kStopped;
194 callback(fit::ok());
195 }
196
197 } // namespace bt::l2cap::testing
198