1 // Copyright 2024 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/fuchsia/host/fidl/channel_server.h"
16
17 #include <algorithm>
18 namespace fidlbt = fuchsia::bluetooth;
19
20 namespace bthost {
21
ChannelServer(fidl::InterfaceRequest<fidlbt::Channel> request,bt::l2cap::Channel::WeakPtr channel,fit::callback<void ()> closed_callback)22 ChannelServer::ChannelServer(fidl::InterfaceRequest<fidlbt::Channel> request,
23 bt::l2cap::Channel::WeakPtr channel,
24 fit::callback<void()> closed_callback)
25 : ServerBase(this, std::move(request)),
26 channel_(std::move(channel)),
27 closed_cb_(std::move(closed_callback)),
28 weak_self_(this) {
29 binding()->set_error_handler(
30 [this](zx_status_t /*status*/) { OnProtocolClosed(); });
31 }
32
~ChannelServer()33 ChannelServer::~ChannelServer() {
34 if (state_ != State::kDeactivated) {
35 bt_log(TRACE, "fidl", "Deactivating channel %u in dtor", channel_->id());
36 Deactivate();
37 }
38 }
39
Send(std::vector<::fuchsia::bluetooth::Packet> packets,SendCallback callback)40 void ChannelServer::Send(std::vector<::fuchsia::bluetooth::Packet> packets,
41 SendCallback callback) {
42 for (auto& fidl_packet : packets) {
43 std::vector<uint8_t>& packet = fidl_packet.packet;
44 if (packet.size() > channel_->max_tx_sdu_size()) {
45 bt_log(TRACE,
46 "fidl",
47 "Dropping %zu bytes for channel %u as max TX SDU is %u ",
48 packet.size(),
49 channel_->id(),
50 channel_->max_tx_sdu_size());
51 continue;
52 }
53
54 // TODO(fxbug.dev/349653544): Avoid making a copy of `packet`, possibly by
55 // making DynamicByteBuffer wrap a std::vector.
56 auto buffer =
57 std::make_unique<bt::DynamicByteBuffer>(bt::BufferView(packet));
58 bool write_success = channel_->Send(std::move(buffer));
59 if (!write_success) {
60 bt_log(TRACE,
61 "fidl",
62 "Failed to write %zu bytes to channel %u",
63 buffer->size(),
64 channel_->id());
65 }
66 }
67
68 fidlbt::Channel_Send_Response response;
69 // NOLINTNEXTLINE(performance-move-const-arg)
70 callback(fidlbt::Channel_Send_Result::WithResponse(std::move(response)));
71 }
72
Receive(ReceiveCallback callback)73 void ChannelServer::Receive(ReceiveCallback callback) {
74 if (receive_cb_) {
75 binding()->Close(ZX_ERR_BAD_STATE);
76 OnProtocolClosed();
77 return;
78 }
79 receive_cb_ = std::move(callback);
80 ServiceReceiveQueue();
81 }
82
WatchChannelParameters(WatchChannelParametersCallback callback)83 void ChannelServer::WatchChannelParameters(
84 WatchChannelParametersCallback callback) {
85 PW_CHECK(
86 !pending_watch_channel_parameters_.has_value(),
87 "WatchChannelParameters called while there was already a pending call.");
88 pending_watch_channel_parameters_ = std::move(callback);
89 }
90
handle_unknown_method(uint64_t ordinal,bool method_has_response)91 void ChannelServer::handle_unknown_method(uint64_t ordinal,
92 bool method_has_response) {
93 bt_log(WARN,
94 "fidl",
95 "ChannelServer: received unknown method (ordinal: %lu)",
96 ordinal);
97 }
98
Activate()99 bool ChannelServer::Activate() {
100 PW_CHECK(state_ == State::kActivating);
101
102 WeakPtr self = weak_self_.GetWeakPtr();
103 bt::l2cap::ChannelId channel_id = channel_->id();
104 bool activate_success = channel_->Activate(
105 [self, channel_id](bt::ByteBufferPtr rx_data) {
106 // Note: this lambda _may_ be invoked immediately for buffered packets.
107 if (self.is_alive()) {
108 self->OnChannelDataReceived(std::move(rx_data));
109 } else {
110 bt_log(
111 TRACE,
112 "fidl",
113 "Ignoring data received on destroyed server (channel_id=%#.4x)",
114 channel_id);
115 }
116 },
117 [self, channel_id] {
118 if (self.is_alive()) {
119 self->OnChannelClosed();
120 } else {
121 bt_log(
122 TRACE,
123 "fidl",
124 "Ignoring channel closure on destroyed server (channel_id=%#.4x)",
125 channel_id);
126 }
127 });
128 if (!activate_success) {
129 return false;
130 }
131
132 state_ = State::kActivated;
133 return true;
134 }
135
Deactivate()136 void ChannelServer::Deactivate() {
137 PW_CHECK(state_ != State::kDeactivated);
138 state_ = State::kDeactivating;
139
140 if (!receive_queue_.empty()) {
141 bt_log(DEBUG,
142 "fidl",
143 "Dropping %zu packets from channel %u due to channel closure",
144 receive_queue_.size(),
145 channel_->id());
146 receive_queue_.clear();
147 }
148 channel_->Deactivate();
149 binding()->Close(ZX_ERR_CONNECTION_RESET);
150
151 state_ = State::kDeactivated;
152 }
153
OnChannelDataReceived(bt::ByteBufferPtr rx_data)154 void ChannelServer::OnChannelDataReceived(bt::ByteBufferPtr rx_data) {
155 // Note: kActivating is deliberately permitted, as ChannelImpl::Activate()
156 // will synchronously deliver any queued frames.
157 PW_CHECK(state_ != State::kDeactivated);
158 if (state_ == State::kDeactivating) {
159 bt_log(DEBUG,
160 "fidl",
161 "Ignoring %s for channel %u while deactivating",
162 __func__,
163 channel_->id());
164 return;
165 }
166
167 PW_CHECK(rx_data);
168 if (rx_data->size() == 0) {
169 bt_log(
170 DEBUG, "fidl", "Ignoring empty rx_data for channel %u", channel_->id());
171 return;
172 }
173
174 PW_CHECK(receive_queue_.size() <= receive_queue_max_frames_);
175 // On a full queue, we drop the oldest element, on the theory that newer data
176 // is more useful. This should be true, e.g., for real-time applications such
177 // as voice calls. In the future, we may want to make the drop-head vs.
178 // drop-tail choice configurable.
179 if (receive_queue_.size() == receive_queue_max_frames_) {
180 // TODO(fxbug.dev/42082614): Add a metric for number of dropped frames.
181 receive_queue_.pop_front();
182 }
183
184 receive_queue_.push_back(std::move(rx_data));
185 ServiceReceiveQueue();
186 }
187
OnChannelClosed()188 void ChannelServer::OnChannelClosed() {
189 if (state_ == State::kDeactivating) {
190 bt_log(DEBUG,
191 "fidl",
192 "Ignoring %s for channel %u while deactivating",
193 __func__,
194 channel_->id());
195 return;
196 }
197 PW_CHECK(state_ == State::kActivated);
198 DeactivateAndRequestDestruction();
199 }
200
OnProtocolClosed()201 void ChannelServer::OnProtocolClosed() { DeactivateAndRequestDestruction(); }
202
DeactivateAndRequestDestruction()203 void ChannelServer::DeactivateAndRequestDestruction() {
204 Deactivate();
205 // closed_cb_ is expected to destroy `this`, so move the callback first.
206 auto closed_cb = std::move(closed_cb_);
207 closed_cb();
208 }
209
ServiceReceiveQueue()210 void ChannelServer::ServiceReceiveQueue() {
211 if (!receive_cb_ || receive_queue_.empty()) {
212 return;
213 }
214 std::vector<uint8_t> buffer = receive_queue_.front()->ToVector();
215 receive_queue_.pop_front();
216
217 ::fuchsia::bluetooth::Channel_Receive_Response response(
218 {fidlbt::Packet{std::move(buffer)}});
219 receive_cb_(
220 fidlbt::Channel_Receive_Result::WithResponse(std::move(response)));
221 receive_cb_ = nullptr;
222 }
223
Create(fidl::InterfaceRequest<fidlbt::Channel> request,bt::l2cap::Channel::WeakPtr channel,fit::callback<void ()> closed_callback)224 std::unique_ptr<ChannelServer> ChannelServer::Create(
225 fidl::InterfaceRequest<fidlbt::Channel> request,
226 bt::l2cap::Channel::WeakPtr channel,
227 fit::callback<void()> closed_callback) {
228 if (!channel.is_alive()) {
229 return nullptr;
230 }
231
232 std::unique_ptr<ChannelServer> server(new ChannelServer(
233 std::move(request), std::move(channel), std::move(closed_callback)));
234
235 if (!server->Activate()) {
236 return nullptr;
237 }
238 return server;
239 }
240 } // namespace bthost
241