xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_remote_service_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/gatt_remote_service_server.h"
16 
17 #include <algorithm>
18 
19 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
20 #include "pw_bluetooth_sapphire/internal/host/att/att.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
22 
23 using fuchsia::bluetooth::ErrorCode;
24 using fuchsia::bluetooth::Status;
25 using fuchsia::bluetooth::gatt::Characteristic;
26 using fuchsia::bluetooth::gatt::CharacteristicPtr;
27 using fuchsia::bluetooth::gatt::Descriptor;
28 using fuchsia::bluetooth::gatt::WriteOptions;
29 
30 using bt::ByteBuffer;
31 using bt::MutableBufferView;
32 using bt::gatt::CharacteristicData;
33 using bt::gatt::CharacteristicHandle;
34 using bt::gatt::DescriptorData;
35 using bt::gatt::DescriptorHandle;
36 using bthost::fidl_helpers::CharacteristicHandleFromFidl;
37 using bthost::fidl_helpers::DescriptorHandleFromFidl;
38 
39 namespace bthost {
40 namespace {
41 
42 // We mask away the "extended properties" property. We expose extended
43 // properties in the same bitfield.
44 constexpr uint8_t kPropertyMask = 0x7F;
45 
CharacteristicToFidl(const CharacteristicData & characteristic,const std::map<DescriptorHandle,DescriptorData> & descriptors)46 Characteristic CharacteristicToFidl(
47     const CharacteristicData& characteristic,
48     const std::map<DescriptorHandle, DescriptorData>& descriptors) {
49   Characteristic fidl_char;
50   fidl_char.id = static_cast<uint64_t>(characteristic.value_handle);
51   fidl_char.type = characteristic.type.ToString();
52   fidl_char.properties =
53       static_cast<uint16_t>(characteristic.properties & kPropertyMask);
54   fidl_char.descriptors.emplace();  // initialize an empty vector
55 
56   // TODO(armansito): Add extended properties.
57 
58   for (const auto& [id, descr] : descriptors) {
59     Descriptor fidl_descr;
60     fidl_descr.id = static_cast<uint64_t>(id.value);
61     fidl_descr.type = descr.type.ToString();
62     fidl_char.descriptors->push_back(std::move(fidl_descr));
63   }
64 
65   return fidl_char;
66 }
67 
NopStatusCallback(bt::att::Result<>)68 void NopStatusCallback(bt::att::Result<>) {}
69 
70 }  // namespace
71 
GattRemoteServiceServer(bt::gatt::RemoteService::WeakPtr service,bt::gatt::GATT::WeakPtr gatt,bt::PeerId peer_id,fidl::InterfaceRequest<fuchsia::bluetooth::gatt::RemoteService> request)72 GattRemoteServiceServer::GattRemoteServiceServer(
73     bt::gatt::RemoteService::WeakPtr service,
74     bt::gatt::GATT::WeakPtr gatt,
75     bt::PeerId peer_id,
76     fidl::InterfaceRequest<fuchsia::bluetooth::gatt::RemoteService> request)
77     : GattServerBase(gatt, this, std::move(request)),
78       service_(std::move(service)),
79       peer_id_(peer_id),
80       weak_self_(this) {
81   PW_DCHECK(service_.is_alive());
82 }
83 
~GattRemoteServiceServer()84 GattRemoteServiceServer::~GattRemoteServiceServer() {
85   for (const auto& iter : notify_handlers_) {
86     if (iter.second != bt::gatt::kInvalidId) {
87       service_->DisableNotifications(
88           iter.first, iter.second, NopStatusCallback);
89     }
90   }
91 }
92 
DiscoverCharacteristics(DiscoverCharacteristicsCallback callback)93 void GattRemoteServiceServer::DiscoverCharacteristics(
94     DiscoverCharacteristicsCallback callback) {
95   auto res_cb = [callback = std::move(callback)](bt::att::Result<> status,
96                                                  const auto& chrcs) {
97     std::vector<Characteristic> fidl_chrcs;
98     if (status.is_ok()) {
99       for (const auto& [id, chrc] : chrcs) {
100         auto& [chr, descs] = chrc;
101         fidl_chrcs.push_back(CharacteristicToFidl(chr, descs));
102       }
103     }
104 
105     callback(fidl_helpers::ResultToFidlDeprecated(status, ""),
106              std::move(fidl_chrcs));
107   };
108 
109   service_->DiscoverCharacteristics(std::move(res_cb));
110 }
111 
ReadCharacteristic(uint64_t id,ReadCharacteristicCallback callback)112 void GattRemoteServiceServer::ReadCharacteristic(
113     uint64_t id, ReadCharacteristicCallback callback) {
114   auto cb = [callback = std::move(callback)](
115                 bt::att::Result<> status, const bt::ByteBuffer& value, auto) {
116     // We always reply with a non-null value.
117     std::vector<uint8_t> vec;
118 
119     if (status.is_ok() && value.size()) {
120       vec.resize(value.size());
121 
122       MutableBufferView vec_view(vec.data(), vec.size());
123       value.Copy(&vec_view);
124     }
125 
126     callback(fidl_helpers::ResultToFidlDeprecated(status), std::move(vec));
127   };
128 
129   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
130   // bt::att:Handle. Fix this.
131   service_->ReadCharacteristic(CharacteristicHandleFromFidl(id), std::move(cb));
132 }
133 
ReadLongCharacteristic(uint64_t id,uint16_t offset,uint16_t max_bytes,ReadLongCharacteristicCallback callback)134 void GattRemoteServiceServer::ReadLongCharacteristic(
135     uint64_t id,
136     uint16_t offset,
137     uint16_t max_bytes,
138     ReadLongCharacteristicCallback callback) {
139   auto cb = [callback = std::move(callback)](
140                 bt::att::Result<> status, const bt::ByteBuffer& value, auto) {
141     // We always reply with a non-null value.
142     std::vector<uint8_t> vec;
143 
144     if (status.is_ok() && value.size()) {
145       vec.resize(value.size());
146 
147       MutableBufferView vec_view(vec.data(), vec.size());
148       value.Copy(&vec_view);
149     }
150 
151     callback(fidl_helpers::ResultToFidlDeprecated(status), std::move(vec));
152   };
153 
154   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
155   // bt::att:Handle. Fix this.
156   service_->ReadLongCharacteristic(
157       CharacteristicHandleFromFidl(id), offset, max_bytes, std::move(cb));
158 }
159 
WriteCharacteristic(uint64_t id,::std::vector<uint8_t> value,WriteCharacteristicCallback callback)160 void GattRemoteServiceServer::WriteCharacteristic(
161     uint64_t id,
162     ::std::vector<uint8_t> value,
163     WriteCharacteristicCallback callback) {
164   auto cb = [callback = std::move(callback)](bt::att::Result<> status) {
165     callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
166   };
167 
168   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
169   // bt::att:Handle. Fix this.
170   service_->WriteCharacteristic(
171       CharacteristicHandleFromFidl(id), std::move(value), std::move(cb));
172 }
173 
WriteLongCharacteristic(uint64_t id,uint16_t offset,::std::vector<uint8_t> value,WriteOptions write_options,WriteLongCharacteristicCallback callback)174 void GattRemoteServiceServer::WriteLongCharacteristic(
175     uint64_t id,
176     uint16_t offset,
177     ::std::vector<uint8_t> value,
178     WriteOptions write_options,
179     WriteLongCharacteristicCallback callback) {
180   auto cb = [callback = std::move(callback)](bt::att::Result<> status) {
181     callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
182   };
183 
184   auto reliable_mode = fidl_helpers::ReliableModeFromFidl(write_options);
185   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
186   // bt::att:Handle. Fix this.
187   service_->WriteLongCharacteristic(CharacteristicHandleFromFidl(id),
188                                     offset,
189                                     std::move(value),
190                                     std::move(reliable_mode),
191                                     std::move(cb));
192 }
193 
WriteCharacteristicWithoutResponse(uint64_t id,::std::vector<uint8_t> value)194 void GattRemoteServiceServer::WriteCharacteristicWithoutResponse(
195     uint64_t id, ::std::vector<uint8_t> value) {
196   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
197   // bt::att:Handle. Fix this.
198   service_->WriteCharacteristicWithoutResponse(CharacteristicHandleFromFidl(id),
199                                                std::move(value),
200                                                /*cb=*/[](auto) {});
201 }
202 
ReadDescriptor(uint64_t id,ReadDescriptorCallback callback)203 void GattRemoteServiceServer::ReadDescriptor(uint64_t id,
204                                              ReadDescriptorCallback callback) {
205   auto cb = [callback = std::move(callback)](
206                 bt::att::Result<> status, const bt::ByteBuffer& value, auto) {
207     // We always reply with a non-null value.
208     std::vector<uint8_t> vec;
209 
210     if (status.is_ok() && value.size()) {
211       vec.resize(value.size());
212 
213       MutableBufferView vec_view(vec.data(), vec.size());
214       value.Copy(&vec_view);
215     }
216 
217     callback(fidl_helpers::ResultToFidlDeprecated(status), std::move(vec));
218   };
219 
220   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
221   // bt::att:Handle. Fix this.
222   service_->ReadDescriptor(DescriptorHandleFromFidl(id), std::move(cb));
223 }
224 
ReadLongDescriptor(uint64_t id,uint16_t offset,uint16_t max_bytes,ReadLongDescriptorCallback callback)225 void GattRemoteServiceServer::ReadLongDescriptor(
226     uint64_t id,
227     uint16_t offset,
228     uint16_t max_bytes,
229     ReadLongDescriptorCallback callback) {
230   auto cb = [callback = std::move(callback)](
231                 bt::att::Result<> status, const bt::ByteBuffer& value, auto) {
232     // We always reply with a non-null value.
233     std::vector<uint8_t> vec;
234 
235     if (status.is_ok() && value.size()) {
236       vec.resize(value.size());
237 
238       MutableBufferView vec_view(vec.data(), vec.size());
239       value.Copy(&vec_view);
240     }
241 
242     callback(fidl_helpers::ResultToFidlDeprecated(status), std::move(vec));
243   };
244 
245   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
246   // bt::att:Handle. Fix this.
247   service_->ReadLongDescriptor(
248       DescriptorHandleFromFidl(id), offset, max_bytes, std::move(cb));
249 }
250 
WriteDescriptor(uint64_t id,::std::vector<uint8_t> value,WriteDescriptorCallback callback)251 void GattRemoteServiceServer::WriteDescriptor(
252     uint64_t id,
253     ::std::vector<uint8_t> value,
254     WriteDescriptorCallback callback) {
255   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
256   // bt::att:Handle. Fix this.
257   service_->WriteDescriptor(
258       DescriptorHandleFromFidl(id),
259       std::move(value),
260       [callback = std::move(callback)](bt::att::Result<> status) {
261         callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
262       });
263 }
264 
WriteLongDescriptor(uint64_t id,uint16_t offset,::std::vector<uint8_t> value,WriteLongDescriptorCallback callback)265 void GattRemoteServiceServer::WriteLongDescriptor(
266     uint64_t id,
267     uint16_t offset,
268     ::std::vector<uint8_t> value,
269     WriteLongDescriptorCallback callback) {
270   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
271   // bt::att:Handle. Fix this.
272   service_->WriteLongDescriptor(
273       DescriptorHandleFromFidl(id),
274       offset,
275       std::move(value),
276       [callback = std::move(callback)](bt::att::Result<> status) {
277         callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
278       });
279 }
280 
ReadByType(fuchsia::bluetooth::Uuid uuid,ReadByTypeCallback callback)281 void GattRemoteServiceServer::ReadByType(fuchsia::bluetooth::Uuid uuid,
282                                          ReadByTypeCallback callback) {
283   service_->ReadByType(
284       fidl_helpers::UuidFromFidl(uuid),
285       [self = weak_self_.GetWeakPtr(),
286        cb = std::move(callback),
287        func = __FUNCTION__](
288           bt::att::Result<> status,
289           std::vector<bt::gatt::RemoteService::ReadByTypeResult> results) {
290         if (!self.is_alive()) {
291           return;
292         }
293 
294         if (status.is_error()) {
295           if (status.error_value().is(bt::HostError::kInvalidParameters)) {
296             bt_log(WARN,
297                    "fidl",
298                    "%s: called with invalid parameters, closing FIDL channel "
299                    "(peer: %s)",
300                    func,
301                    bt_str(self->peer_id_));
302             self->binding()->Close(ZX_ERR_INVALID_ARGS);
303             return;
304           }
305           cb(fpromise::error(
306               fidl_helpers::GattErrorToFidl(status.error_value())));
307           return;
308         }
309 
310         if (results.size() >
311             fuchsia::bluetooth::gatt::MAX_READ_BY_TYPE_RESULTS) {
312           cb(fpromise::error(
313               fuchsia::bluetooth::gatt::Error::TOO_MANY_RESULTS));
314           return;
315         }
316 
317         std::vector<fuchsia::bluetooth::gatt::ReadByTypeResult> fidl_results;
318         fidl_results.reserve(results.size());
319 
320         for (const auto& result : results) {
321           fuchsia::bluetooth::gatt::ReadByTypeResult fidl_result;
322           fidl_result.set_id(static_cast<uint64_t>(result.handle.value));
323           if (result.result.is_ok()) {
324             fidl_result.set_value(result.result.value()->ToVector());
325           } else {
326             fidl_result.set_error(fidl_helpers::GattErrorToFidl(
327                 bt::att::Error(result.result.error_value())));
328           }
329           fidl_results.push_back(std::move(fidl_result));
330         }
331 
332         cb(fpromise::ok(std::move(fidl_results)));
333       });
334 }
335 
NotifyCharacteristic(uint64_t id,bool enable,NotifyCharacteristicCallback callback)336 void GattRemoteServiceServer::NotifyCharacteristic(
337     uint64_t id, bool enable, NotifyCharacteristicCallback callback) {
338   // TODO(fxbug.dev/42141942): The 64 bit `id` can overflow the 16 bits of a
339   // bt::att:Handle. Fix this.
340   auto handle = CharacteristicHandleFromFidl(id);
341   if (!enable) {
342     auto iter = notify_handlers_.find(handle);
343     if (iter == notify_handlers_.end()) {
344       callback(fidl_helpers::NewFidlError(ErrorCode::NOT_FOUND,
345                                           "characteristic not notifying"));
346       return;
347     }
348 
349     if (iter->second == bt::gatt::kInvalidId) {
350       callback(fidl_helpers::NewFidlError(
351           ErrorCode::IN_PROGRESS,
352           "characteristic notification registration pending"));
353       return;
354     }
355 
356     service_->DisableNotifications(
357         handle,
358         iter->second,
359         [callback = std::move(callback)](bt::att::Result<> status) {
360           callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
361         });
362     notify_handlers_.erase(iter);
363 
364     return;
365   }
366 
367   if (notify_handlers_.count(handle) != 0) {
368     callback(fidl_helpers::NewFidlError(ErrorCode::ALREADY,
369                                         "characteristic already notifying"));
370     return;
371   }
372 
373   // Prevent any races and leaks by marking a notification is in progress
374   notify_handlers_[handle] = bt::gatt::kInvalidId;
375 
376   auto self = weak_self_.GetWeakPtr();
377   auto value_cb = [self, id](const ByteBuffer& value,
378                              bool /*maybe_truncated*/) {
379     if (!self.is_alive())
380       return;
381 
382     self->binding()->events().OnCharacteristicValueUpdated(id,
383                                                            value.ToVector());
384   };
385 
386   auto status_cb =
387       [self, svc = service_, handle, callback = std::move(callback)](
388           bt::att::Result<> status, HandlerId handler_id) {
389         if (!self.is_alive()) {
390           if (status.is_ok()) {
391             // Disable this handler so it doesn't leak.
392             svc->DisableNotifications(handle, handler_id, NopStatusCallback);
393           }
394 
395           callback(fidl_helpers::NewFidlError(ErrorCode::FAILED, "canceled"));
396           return;
397         }
398 
399         if (status.is_ok()) {
400           PW_DCHECK(handler_id != bt::gatt::kInvalidId);
401           PW_DCHECK(self->notify_handlers_.count(handle) == 1u);
402           PW_DCHECK(self->notify_handlers_[handle] == bt::gatt::kInvalidId);
403           self->notify_handlers_[handle] = handler_id;
404         } else {
405           // Remove our handle holder.
406           self->notify_handlers_.erase(handle);
407         }
408 
409         callback(fidl_helpers::ResultToFidlDeprecated(status, ""));
410       };
411 
412   service_->EnableNotifications(
413       handle, std::move(value_cb), std::move(status_cb));
414 }
415 
416 }  // namespace bthost
417