xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_server_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_server_server.h"
16 
17 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
20 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h"
21 #include "pw_bluetooth_sapphire/internal/host/gatt/connection.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
23 #include "pw_bluetooth_sapphire/internal/host/gatt/server.h"
24 
25 using fuchsia::bluetooth::ErrorCode;
26 using fuchsia::bluetooth::Status;
27 using GattErrorCode = fuchsia::bluetooth::gatt::ErrorCode;
28 
29 using fuchsia::bluetooth::gatt::Characteristic;
30 using fuchsia::bluetooth::gatt::Descriptor;
31 using fuchsia::bluetooth::gatt::LocalService;
32 using fuchsia::bluetooth::gatt::LocalServiceDelegate;
33 using fuchsia::bluetooth::gatt::LocalServiceDelegatePtr;
34 using fuchsia::bluetooth::gatt::SecurityRequirementsPtr;
35 using fuchsia::bluetooth::gatt::ServiceInfo;
36 
37 namespace bthost {
38 namespace {
39 
GattStatusFromFidl(GattErrorCode error_code,bool is_read)40 fit::result<bt::att::ErrorCode> GattStatusFromFidl(GattErrorCode error_code,
41                                                    bool is_read) {
42   switch (error_code) {
43     case GattErrorCode::NO_ERROR:
44       return fit::ok();
45     case GattErrorCode::INVALID_OFFSET:
46       return fit::error(bt::att::ErrorCode::kInvalidOffset);
47     case GattErrorCode::INVALID_VALUE_LENGTH:
48       return fit::error(bt::att::ErrorCode::kInvalidAttributeValueLength);
49     case GattErrorCode::NOT_PERMITTED:
50       if (is_read)
51         return fit::error(bt::att::ErrorCode::kReadNotPermitted);
52       return fit::error(bt::att::ErrorCode::kWriteNotPermitted);
53     default:
54       break;
55   }
56   return fit::error(bt::att::ErrorCode::kUnlikelyError);
57 }
58 
ParseSecurityRequirements(const SecurityRequirementsPtr & reqs)59 bt::att::AccessRequirements ParseSecurityRequirements(
60     const SecurityRequirementsPtr& reqs) {
61   if (!reqs) {
62     return bt::att::AccessRequirements();
63   }
64   return bt::att::AccessRequirements(reqs->encryption_required,
65                                      reqs->authentication_required,
66                                      reqs->authorization_required);
67 }
68 
69 // Carries a either a successful Result or an error message that can be sent as
70 // a FIDL response.
71 template <typename Result,
72           typename Error = std::string,
73           typename = std::enable_if_t<!std::is_same<Result, Error>::value>>
74 struct MaybeResult final {
MaybeResultbthost::__anon5e33449e0111::MaybeResult75   explicit MaybeResult(Result&& result)
76       : result(std::forward<Result>(result)) {}
77 
MaybeResultbthost::__anon5e33449e0111::MaybeResult78   explicit MaybeResult(Error&& error) : error(std::forward<Error>(error)) {}
79 
is_errorbthost::__anon5e33449e0111::MaybeResult80   bool is_error() const { return static_cast<bool>(!result); }
81 
82   Result result;
83   Error error;
84 };
85 
86 using DescriptorResult = MaybeResult<bt::gatt::DescriptorPtr>;
NewDescriptor(const Descriptor & fidl_desc)87 DescriptorResult NewDescriptor(const Descriptor& fidl_desc) {
88   auto read_reqs = ParseSecurityRequirements(fidl_desc.permissions->read);
89   auto write_reqs = ParseSecurityRequirements(fidl_desc.permissions->write);
90 
91   bt::UUID type;
92   if (!bt::StringToUuid(fidl_desc.type, &type)) {
93     return DescriptorResult("Invalid descriptor UUID");
94   }
95 
96   return DescriptorResult(std::make_unique<bt::gatt::Descriptor>(
97       fidl_desc.id, type, read_reqs, write_reqs));
98 }
99 
100 using CharacteristicResult = MaybeResult<bt::gatt::CharacteristicPtr>;
NewCharacteristic(const Characteristic & fidl_chrc)101 CharacteristicResult NewCharacteristic(const Characteristic& fidl_chrc) {
102   uint8_t props = fidl_chrc.properties & 0xFF;
103   uint16_t ext_props = (fidl_chrc.properties & 0xFF00) >> 8;
104 
105   if (!fidl_chrc.permissions) {
106     return CharacteristicResult("Characteristic permissions missing");
107   }
108 
109   bool supports_update = (props & bt::gatt::Property::kNotify) ||
110                          (props & bt::gatt::Property::kIndicate);
111   if (supports_update != static_cast<bool>(fidl_chrc.permissions->update)) {
112     return CharacteristicResult(
113         supports_update ? "Characteristic update permission required"
114                         : "Characteristic update permission must be null");
115   }
116 
117   auto read_reqs = ParseSecurityRequirements(fidl_chrc.permissions->read);
118   auto write_reqs = ParseSecurityRequirements(fidl_chrc.permissions->write);
119   auto update_reqs = ParseSecurityRequirements(fidl_chrc.permissions->update);
120 
121   bt::UUID type;
122   if (!bt::StringToUuid(fidl_chrc.type, &type)) {
123     return CharacteristicResult("Invalid characteristic UUID");
124   }
125 
126   auto chrc = std::make_unique<bt::gatt::Characteristic>(
127       fidl_chrc.id, type, props, ext_props, read_reqs, write_reqs, update_reqs);
128   if (fidl_chrc.descriptors && !fidl_chrc.descriptors->empty()) {
129     for (const auto& fidl_desc : *fidl_chrc.descriptors) {
130       auto desc_result = NewDescriptor(fidl_desc);
131       if (desc_result.is_error()) {
132         return CharacteristicResult(std::move(desc_result.error));
133       }
134 
135       chrc->AddDescriptor(std::move(desc_result.result));
136     }
137   }
138 
139   return CharacteristicResult(std::move(chrc));
140 }
141 
142 }  // namespace
143 
144 // Implements the gatt::LocalService FIDL interface. Instances of this class are
145 // only created by a GattServerServer.
146 class GattServerServer::LocalServiceImpl
147     : public GattServerBase<fuchsia::bluetooth::gatt::LocalService> {
148  public:
LocalServiceImpl(GattServerServer * owner,uint64_t id,LocalServiceDelegatePtr delegate,::fidl::InterfaceRequest<LocalService> request)149   LocalServiceImpl(GattServerServer* owner,
150                    uint64_t id,
151                    LocalServiceDelegatePtr delegate,
152                    ::fidl::InterfaceRequest<LocalService> request)
153       : GattServerBase(owner->gatt(), this, std::move(request)),
154         owner_(owner),
155         id_(id),
156         delegate_(std::move(delegate)) {
157     PW_DCHECK(owner_);
158     PW_DCHECK(delegate_);
159   }
160 
161   // The destructor removes the GATT service
~LocalServiceImpl()162   ~LocalServiceImpl() override {
163     CleanUp();
164 
165     // Do not notify the owner in this case. If we got here it means that
166     // |owner_| deleted us.
167   }
168 
169   // Returns the current delegate. Returns nullptr if the delegate was
170   // disconnected (e.g. due to a call to RemoveService()).
delegate()171   LocalServiceDelegate* delegate() { return delegate_.get(); }
172 
173  private:
174   // fuchsia::bluetooth::gatt::Service overrides:
RemoveService()175   void RemoveService() override {
176     CleanUp();
177     owner_->RemoveService(id_);
178   }
179 
NotifyValue(uint64_t characteristic_id,::std::string peer_id,::std::vector<uint8_t> value,bool confirm)180   void NotifyValue(uint64_t characteristic_id,
181                    ::std::string peer_id,
182                    ::std::vector<uint8_t> value,
183                    bool confirm) override {
184     auto id = fidl_helpers::PeerIdFromString(std::move(peer_id));
185     if (id) {
186       bt::gatt::IndicationCallback indication_cb = nullptr;
187       if (confirm) {
188         indication_cb = [](bt::att::Result<> result) {
189           bt_log(DEBUG, "fidl", "indication result: %s", bt_str(result));
190         };
191       }
192       gatt()->SendUpdate(id_,
193                          characteristic_id,
194                          *id,
195                          std::move(value),
196                          std::move(indication_cb));
197     }
198   }
199 
200   // Unregisters the underlying service if it is still active.
CleanUp()201   void CleanUp() {
202     delegate_ = nullptr;  // Closes the delegate handle.
203     gatt()->UnregisterService(id_);
204   }
205 
206   // |owner_| owns this instance and is expected to outlive it.
207   GattServerServer* owner_;  // weak
208   uint64_t id_;
209 
210   // The delegate connection for the corresponding service instance. This gets
211   // cleared when the service is unregistered (via RemoveService() or
212   // destruction).
213   LocalServiceDelegatePtr delegate_;
214 
215   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LocalServiceImpl);
216 };
217 
GattServerServer(bt::gatt::GATT::WeakPtr gatt,fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Server> request)218 GattServerServer::GattServerServer(
219     bt::gatt::GATT::WeakPtr gatt,
220     fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Server> request)
221     : GattServerBase(gatt, this, std::move(request)), weak_self_(this) {}
222 
~GattServerServer()223 GattServerServer::~GattServerServer() {
224   // This will remove all of our services from their adapter.
225   services_.clear();
226 }
227 
RemoveService(uint64_t id)228 void GattServerServer::RemoveService(uint64_t id) {
229   if (services_.erase(id)) {
230     bt_log(DEBUG, "fidl", "%s: service removed (id: %lu)", __FUNCTION__, id);
231   } else {
232     bt_log(WARN, "fidl", "%s: service id not found: %lu", __FUNCTION__, id);
233   }
234 }
235 
PublishService(ServiceInfo service_info,fidl::InterfaceHandle<LocalServiceDelegate> delegate,fidl::InterfaceRequest<LocalService> service_iface,PublishServiceCallback callback)236 void GattServerServer::PublishService(
237     ServiceInfo service_info,
238     fidl::InterfaceHandle<LocalServiceDelegate> delegate,
239     fidl::InterfaceRequest<LocalService> service_iface,
240     PublishServiceCallback callback) {
241   if (!delegate) {
242     bt_log(WARN, "fidl", "%s: missing service delegate", __FUNCTION__);
243     auto error = fidl_helpers::NewFidlError(ErrorCode::INVALID_ARGUMENTS,
244                                             "A delegate is required");
245     callback(std::move(error));
246     return;
247   }
248 
249   if (!service_iface) {
250     bt_log(WARN, "fidl", "%s: missing service interface request", __FUNCTION__);
251     auto error = fidl_helpers::NewFidlError(ErrorCode::INVALID_ARGUMENTS,
252                                             "Service interface is required");
253     callback(std::move(error));
254     return;
255   }
256 
257   bt::UUID service_type;
258   if (!bt::StringToUuid(service_info.type, &service_type)) {
259     bt_log(WARN,
260            "fidl",
261            "%s: invalid service UUID %s",
262            __FUNCTION__,
263            service_info.type.c_str());
264     auto error = fidl_helpers::NewFidlError(ErrorCode::INVALID_ARGUMENTS,
265                                             "Invalid service UUID");
266     callback(std::move(error));
267     return;
268   }
269 
270   // Process the FIDL service tree.
271   auto service =
272       std::make_unique<bt::gatt::Service>(service_info.primary, service_type);
273   if (service_info.characteristics) {
274     for (const auto& fidl_chrc : *service_info.characteristics) {
275       auto chrc_result = NewCharacteristic(fidl_chrc);
276       if (chrc_result.is_error()) {
277         auto error = fidl_helpers::NewFidlError(ErrorCode::INVALID_ARGUMENTS,
278                                                 chrc_result.error);
279         callback(std::move(error));
280         return;
281       }
282 
283       service->AddCharacteristic(std::move(chrc_result.result));
284     }
285   }
286 
287   auto self = weak_self_.GetWeakPtr();
288 
289   // Set up event handlers.
290   auto read_handler = [self](bt::PeerId /*ignore*/,
291                              auto svc_id,
292                              auto id,
293                              auto offset,
294                              auto responder) mutable {
295     if (self.is_alive()) {
296       self->OnReadRequest(svc_id, id, offset, std::move(responder));
297     } else {
298       responder(fit::error(bt::att::ErrorCode::kUnlikelyError),
299                 bt::BufferView());
300     }
301   };
302   auto write_handler = [self](bt::PeerId /*ignore*/,
303                               auto svc_id,
304                               auto id,
305                               auto offset,
306                               const auto& value,
307                               auto responder) mutable {
308     if (self.is_alive()) {
309       self->OnWriteRequest(svc_id, id, offset, value, std::move(responder));
310     } else {
311       responder(fit::error(bt::att::ErrorCode::kUnlikelyError));
312     }
313   };
314   auto ccc_callback = [self](auto svc_id,
315                              auto id,
316                              bt::gatt::PeerId peer_id,
317                              bool notify,
318                              bool indicate) {
319     if (self.is_alive())
320       self->OnCharacteristicConfig(svc_id, id, peer_id, notify, indicate);
321   };
322 
323   auto id_cb = [self,
324                 delegate = std::move(delegate),
325                 service_iface = std::move(service_iface),
326                 callback = std::move(callback)](bt::gatt::IdType id) mutable {
327     if (!self.is_alive())
328       return;
329 
330     if (!id) {
331       // TODO(armansito): Report a more detailed string if registration
332       // fails due to duplicate ids.
333       auto error = fidl_helpers::NewFidlError(ErrorCode::FAILED,
334                                               "Failed to publish service");
335       callback(std::move(error));
336       return;
337     }
338 
339     PW_DCHECK(self->services_.find(id) == self->services_.end());
340 
341     // This will be called if either the delegate or the service connection
342     // closes.
343     auto connection_error_cb = [self, id](zx_status_t status) {
344       bt_log(DEBUG, "bt-host", "removing GATT service (id: %lu)", id);
345       if (self.is_alive())
346         self->RemoveService(id);
347     };
348 
349     auto delegate_ptr = delegate.Bind();
350     delegate_ptr.set_error_handler(connection_error_cb);
351 
352     auto service_server = std::make_unique<LocalServiceImpl>(
353         &self.get(), id, std::move(delegate_ptr), std::move(service_iface));
354     service_server->set_error_handler(connection_error_cb);
355     self->services_[id] = std::move(service_server);
356 
357     callback(Status());
358   };
359 
360   gatt()->RegisterService(std::move(service),
361                           std::move(id_cb),
362                           std::move(read_handler),
363                           std::move(write_handler),
364                           std::move(ccc_callback));
365 }
366 
OnReadRequest(bt::gatt::IdType service_id,bt::gatt::IdType id,uint16_t offset,bt::gatt::ReadResponder responder)367 void GattServerServer::OnReadRequest(bt::gatt::IdType service_id,
368                                      bt::gatt::IdType id,
369                                      uint16_t offset,
370                                      bt::gatt::ReadResponder responder) {
371   auto iter = services_.find(service_id);
372   if (iter == services_.end()) {
373     bt_log(
374         WARN, "fidl", "%s: unknown service id %lu", __FUNCTION__, service_id);
375     responder(fit::error(bt::att::ErrorCode::kUnlikelyError), bt::BufferView());
376     return;
377   }
378 
379   auto cb = [responder = std::move(responder)](
380                 fidl::VectorPtr<uint8_t> optional_value,
381                 auto error_code) mutable {
382     std::vector<uint8_t> value;
383     if (optional_value.has_value()) {
384       value = std::move(optional_value.value());
385     }
386     responder(GattStatusFromFidl(error_code, /*is_read=*/true),
387               bt::BufferView(value.data(), value.size()));
388   };
389 
390   auto* delegate = iter->second->delegate();
391   PW_DCHECK(delegate);
392   delegate->OnReadValue(id, offset, std::move(cb));
393 }
394 
OnWriteRequest(bt::gatt::IdType service_id,bt::gatt::IdType id,uint16_t offset,const bt::ByteBuffer & value,bt::gatt::WriteResponder responder)395 void GattServerServer::OnWriteRequest(bt::gatt::IdType service_id,
396                                       bt::gatt::IdType id,
397                                       uint16_t offset,
398                                       const bt::ByteBuffer& value,
399                                       bt::gatt::WriteResponder responder) {
400   auto iter = services_.find(service_id);
401   if (iter == services_.end()) {
402     bt_log(
403         WARN, "fidl", "%s: unknown service id %lu", __FUNCTION__, service_id);
404     responder(fit::error(bt::att::ErrorCode::kUnlikelyError));
405     return;
406   }
407 
408   auto fidl_value = fidl::To<std::vector<uint8_t>>(value);
409   auto* delegate = iter->second->delegate();
410   PW_DCHECK(delegate);
411 
412   if (!responder) {
413     delegate->OnWriteWithoutResponse(id, offset, std::move(fidl_value));
414     return;
415   }
416 
417   auto cb = [responder = std::move(responder)](auto error_code) mutable {
418     responder(GattStatusFromFidl(error_code, /*is_read=*/false));
419   };
420 
421   delegate->OnWriteValue(id, offset, std::move(fidl_value), std::move(cb));
422 }
423 
OnCharacteristicConfig(bt::gatt::IdType service_id,bt::gatt::IdType chrc_id,bt::gatt::PeerId peer_id,bool notify,bool indicate)424 void GattServerServer::OnCharacteristicConfig(bt::gatt::IdType service_id,
425                                               bt::gatt::IdType chrc_id,
426                                               bt::gatt::PeerId peer_id,
427                                               bool notify,
428                                               bool indicate) {
429   auto iter = services_.find(service_id);
430   if (iter == services_.end()) {
431     bt_log(
432         WARN, "fidl", "%s: unknown service id %lu", __FUNCTION__, service_id);
433     return;
434   }
435 
436   auto* delegate = iter->second->delegate();
437   PW_DCHECK(delegate);
438   delegate->OnCharacteristicConfiguration(
439       chrc_id, peer_id.ToString(), notify, indicate);
440 }
441 
442 }  // namespace bthost
443