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