xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_client_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_client_server.h"
16 
17 #include <lib/fit/defer.h>
18 
19 #include <utility>
20 
21 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_remote_service_server.h"
22 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
24 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h"
25 
26 using fuchsia::bluetooth::ErrorCode;
27 using fuchsia::bluetooth::Status;
28 
29 using fuchsia::bluetooth::gatt::Client;
30 using fuchsia::bluetooth::gatt::RemoteService;
31 using fuchsia::bluetooth::gatt::ServiceInfo;
32 using fuchsia::bluetooth::gatt::ServiceInfoPtr;
33 
34 namespace bthost {
35 
GattClientServer(bt::gatt::PeerId peer_id,bt::gatt::GATT::WeakPtr gatt,fidl::InterfaceRequest<Client> request)36 GattClientServer::GattClientServer(bt::gatt::PeerId peer_id,
37                                    bt::gatt::GATT::WeakPtr gatt,
38                                    fidl::InterfaceRequest<Client> request)
39     : GattServerBase(std::move(gatt), this, std::move(request)),
40       peer_id_(peer_id),
41       weak_self_(this) {}
42 
ListServices(::fidl::VectorPtr<::std::string> fidl_uuids,ListServicesCallback callback)43 void GattClientServer::ListServices(::fidl::VectorPtr<::std::string> fidl_uuids,
44                                     ListServicesCallback callback) {
45   // Parse the UUID list.
46   std::vector<bt::UUID> uuids;
47   if (fidl_uuids.has_value()) {
48     // Allocate all at once and convert in-place.
49     uuids.resize(fidl_uuids->size());
50     for (size_t i = 0; i < uuids.size(); ++i) {
51       if (!StringToUuid(fidl_uuids.value()[i], &uuids[i])) {
52         bt_log(WARN,
53                "fidl",
54                "%s: Invalid UUID: %s (peer: %s)",
55                __FUNCTION__,
56                fidl_uuids.value()[i].c_str(),
57                bt_str(peer_id_));
58         callback(fidl_helpers::NewFidlError(
59                      ErrorCode::INVALID_ARGUMENTS,
60                      "Invalid UUID: " + fidl_uuids.value()[i]),
61                  std::vector<ServiceInfo>(static_cast<size_t>(0u)));
62         return;
63       }
64     }
65   }
66 
67   auto cb = [callback = std::move(callback),
68              peer_id = peer_id_,
69              func = __FUNCTION__](bt::att::Result<> status, auto services) {
70     std::vector<ServiceInfo> out;
71     if (status.is_error()) {
72       bt_log(WARN,
73              "fidl",
74              "%s: Failed to discover services (peer: %s)",
75              func,
76              bt_str(peer_id));
77       auto fidl_status = fidl_helpers::ResultToFidlDeprecated(
78           status, "Failed to discover services");
79       callback(std::move(fidl_status), std::move(out));
80       return;
81     }
82 
83     out.resize(services.size());
84 
85     size_t i = 0;
86     for (const auto& svc : services) {
87       ServiceInfo service_info;
88       service_info.id = svc->handle();
89       service_info.primary = svc->info().kind == bt::gatt::ServiceKind::PRIMARY;
90       service_info.type = svc->uuid().ToString();
91       out[i++] = std::move(service_info);
92     }
93     callback(Status(), std::move(out));
94   };
95 
96   gatt()->ListServices(peer_id_, std::move(uuids), std::move(cb));
97 }
98 
ConnectToService(uint64_t id,::fidl::InterfaceRequest<RemoteService> request)99 void GattClientServer::ConnectToService(
100     uint64_t id, ::fidl::InterfaceRequest<RemoteService> request) {
101   if (connected_services_.count(id)) {
102     bt_log(WARN,
103            "fidl",
104            "%s: service already requested (service: %lu, peer: %s)",
105            __FUNCTION__,
106            id,
107            bt_str(peer_id_));
108     return;
109   }
110 
111   // Initialize an entry so that we remember when this request is in progress.
112   connected_services_[id] = nullptr;
113 
114   bt::gatt::RemoteService::WeakPtr service = gatt()->FindService(peer_id_, id);
115 
116   // Automatically called on failure.
117   auto fail_cleanup = fit::defer([this, id] { connected_services_.erase(id); });
118 
119   if (!service.is_alive()) {
120     bt_log(WARN,
121            "fidl",
122            "%s: failed (service: %lu, peer: %s)",
123            __FUNCTION__,
124            id,
125            bt_str(peer_id_));
126     return;
127   }
128 
129   // Clean up the server if either the peer device or the FIDL client
130   // disconnects.
131   auto self = weak_self_.GetWeakPtr();
132   const char* func = __FUNCTION__;
133   auto error_cb = [self, id, peer_id = peer_id_, func] {
134     bt_log(DEBUG,
135            "fidl",
136            "%s: service disconnected (service: %lu, peer: %s)",
137            func,
138            id,
139            bt_str(peer_id));
140     if (self.is_alive()) {
141       self->connected_services_.erase(id);
142     }
143   };
144 
145   if (!service->AddRemovedHandler(error_cb)) {
146     bt_log(WARN,
147            "fidl",
148            "%s: failed to assign closed handler (service: %lu, peer: %s)",
149            func,
150            id,
151            bt_str(self->peer_id_));
152     return;
153   }
154 
155   fail_cleanup.cancel();
156 
157   auto server = std::make_unique<GattRemoteServiceServer>(
158       service->GetWeakPtr(), gatt(), peer_id_, std::move(request));
159   server->set_error_handler(
160       [cb = std::move(error_cb)](zx_status_t status) { cb(); });
161 
162   self->connected_services_[id] = std::move(server);
163 }
164 
165 }  // namespace bthost
166