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