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