xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/channel_server.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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