1use bt_common::{PeerId, Uuid};
6use bt_gatt::*;
7use fidl::EventPair;
8use fidl::client::QueryResponseFut;
9use fidl::endpoints::RequestStream;
10use fidl_fuchsia_bluetooth as fidl_bt;
11use fidl_fuchsia_bluetooth_gatt2 as fidl_gatt2;
12use fidl_fuchsia_bluetooth_le as fidl_le;
13use fidl_gatt2::{
14 LocalServiceControlHandle, LocalServiceRequestStream, ServerPublishServiceResult,
15 ValueChangedParameters,
16};
17use fidl_le::{ConnectionProxy, ScanResultWatcherProxy};
18use fuchsia_async::{self as fasync, TimeoutExt};
19use fuchsia_sync::Mutex;
20use futures::future::{FusedFuture, Ready};
21use futures::stream::FusedStream;
22use futures::{Future, FutureExt, Stream, StreamExt};
23use std::collections::{HashMap, VecDeque};
24use std::pin::Pin;
25use std::sync::Arc;
26use std::task::Poll;
27use zx;
28
29#[cfg(test)]
30mod test;
31
32pub mod pii;
33
34mod periodic_advertising;
35pub use periodic_advertising::PeriodicAdvertising;
36
37pub struct FuchsiaTypes {}
38
39impl bt_gatt::GattTypes for FuchsiaTypes {
40 type Central = Central;
41 type ScanResultStream = ScanResultStream;
42 type Client = Client;
43 type ConnectFuture = Ready<Result<Self::Client>>;
44 type PeriodicAdvertising = PeriodicAdvertising;
45
46 type PeerServiceHandle = PeerServiceHandle;
47 type FindServicesFut = fasync::Task<Result<Vec<PeerServiceHandle>>>;
48 type PeerService = PeerService;
49 type ServiceConnectFut = Ready<Result<PeerService>>;
50
51 type ReadFut<'a> = ReadFuture<'a>;
52 type WriteFut<'a> = WriteFuture<'a>;
53 type CharacteristicDiscoveryFut = CharacteristicResultFut;
54 type NotificationStream = CharacteristicNotificationStream;
55}
56
57impl bt_gatt::ServerTypes for FuchsiaTypes {
58 type Server = Server;
59 type LocalService = LocalService;
60 type LocalServiceFut = LocalServiceFut;
61 type ServiceEventStream = LocalEventStream;
62 type ServiceWriteType = Vec<u8>;
63 type ReadResponder = ReadResponder;
64 type WriteResponder = WriteResponder;
65 type IndicateConfirmationStream = IndicateConfirmationStream;
66}
67
68#[derive(Clone)]
69pub struct Central {
70 proxy: fidl_le::CentralProxy,
71}
72
73impl Central {
74 pub fn new(proxy: fidl_le::CentralProxy) -> Self {
75 Self { proxy }
76 }
77}
78
79pub(crate) fn to_fidl_peer_id(id: &PeerId) -> fidl_fuchsia_bluetooth::PeerId {
80 fidl_fuchsia_bluetooth::PeerId { value: id.0 }
81}
82
83fn filter_into_fidl(filter: ¢ral::ScanFilter) -> fidl_le::Filter {
84 use central::Filter::*;
85 let mut fidl_filter = fidl_le::Filter::default();
86 for filter in &filter.filters {
87 match filter {
88 ServiceUuid(uuid) => {
89 fidl_filter.service_uuid = Some(to_fidl_uuid(uuid));
90 }
91 HasServiceData(uuid) => {
92 fidl_filter.service_data_uuid = Some(to_fidl_uuid(uuid));
93 }
94 HasManufacturerData(id) => fidl_filter.manufacturer_id = Some(*id),
95 IsConnectable => fidl_filter.connectable = Some(true),
96 MatchesName(partial_name) => fidl_filter.name = Some(partial_name.clone()),
97 MaxPathLoss(path_loss) => fidl_filter.max_path_loss = Some(*path_loss),
98 }
99 }
100 fidl_filter
101}
102
103pub(crate) fn to_fidl_uuid(uuid: &Uuid) -> fidl_fuchsia_bluetooth::Uuid {
104 let uuid: uuid::Uuid = (*uuid).into();
105 let uuid: fuchsia_bluetooth::types::Uuid = uuid.into();
106 uuid.into()
107}
108
109impl bt_gatt::Central<FuchsiaTypes> for Central {
110 fn scan(&self, filters: &[central::ScanFilter]) -> ScanResultStream {
111 let scan_options = fidl_le::ScanOptions {
112 filters: Some(filters.iter().map(filter_into_fidl).collect()),
113 ..Default::default()
114 };
115 let (proxy, server_end) =
116 fidl::endpoints::create_proxy::<fidl_le::ScanResultWatcherMarker>();
117 let scan_stopped_fut = self.proxy.scan(&scan_options, server_end);
118 ScanResultStream::new(proxy, scan_stopped_fut)
119 }
120
121 fn periodic_advertising(
122 &self,
123 ) -> bt_gatt::Result<<FuchsiaTypes as GattTypes>::PeriodicAdvertising> {
124 Ok(PeriodicAdvertising { proxy: self.proxy.clone() })
125 }
126
127 fn connect(&self, peer_id: PeerId) -> <FuchsiaTypes as GattTypes>::ConnectFuture {
128 use futures::future::ready;
129 let (proxy, server_end) = fidl::endpoints::create_proxy::<fidl_le::ConnectionMarker>();
130 if let Err(e) =
131 self.proxy.connect(&to_fidl_peer_id(&peer_id), &Default::default(), server_end)
132 {
133 return ready(Err(types::Error::Other(Box::new(e))));
134 }
135 let (client_proxy, server_end) =
136 fidl::endpoints::create_proxy::<fidl_gatt2::ClientMarker>();
137 if let Err(e) = proxy.request_gatt_client(server_end) {
138 return ready(Err(types::Error::Other(Box::new(e))));
139 }
140 return ready(Ok(Client::new(peer_id, proxy, client_proxy)));
141 }
142}
143
144pub fn to_gatt_uuid(uuid: &fidl_bt::Uuid) -> Uuid {
145 let uuid: fuchsia_bluetooth::types::Uuid = uuid.into();
146 let uuid: uuid::Uuid = uuid.into();
147 uuid.into()
148}
149
150pub fn to_gatt_peer_id(id: &fidl_bt::PeerId) -> bt_common::PeerId {
151 bt_common::PeerId(id.value)
152}
153
154fn to_gatt_gatt_error(err: &fidl_gatt2::Error) -> bt_gatt::types::Error {
155 match bt_gatt::types::GattError::try_from(*err as u32) {
156 Ok(gatt_er) => gatt_er.into(),
157 Err(e) => e,
158 }
159}
160
161fn to_fidl_gatt_error(err: &bt_gatt::types::GattError) -> fidl_gatt2::Error {
162 fidl_gatt2::Error::from_primitive(*err as u32).unwrap()
164}
165
166fn to_fidl_writemode(mode: &bt_gatt::types::WriteMode) -> fidl_gatt2::WriteMode {
167 use bt_gatt::types::WriteMode;
168 match mode {
169 WriteMode::None => fidl_gatt2::WriteMode::Default,
170 WriteMode::Reliable => fidl_gatt2::WriteMode::Reliable,
171 WriteMode::WithoutResponse => fidl_gatt2::WriteMode::WithoutResponse,
172 }
173}
174
175fn to_gatt_advertising_data(
176 data: fidl_le::AdvertisingData,
177) -> Vec<bt_gatt::central::AdvertisingDatum> {
178 use bt_gatt::central::AdvertisingDatum::*;
179 let mut ret = Vec::new();
180 if let Some(appearance) = data.appearance {
181 ret.push(Appearance(appearance.into_primitive()));
182 }
183 if let Some(level) = data.tx_power_level {
184 ret.push(TxPowerLevel(level));
185 }
186 if let Some(uuids) = data.service_uuids {
187 ret.push(Services(uuids.iter().map(to_gatt_uuid).collect()));
188 }
189 if let Some(datas) = data.service_data {
190 let mut datas = datas
191 .into_iter()
192 .map(|fidl_le::ServiceData { uuid, data }| ServiceData(to_gatt_uuid(&uuid), data))
193 .collect();
194 ret.append(&mut datas);
195 }
196 if let Some(manuf_data) = data.manufacturer_data {
197 let mut manufs = manuf_data
198 .into_iter()
199 .map(|fidl_le::ManufacturerData { company_id, data }| {
200 ManufacturerData(company_id, data)
201 })
202 .collect();
203 ret.append(&mut manufs);
204 }
205 if let Some(uris) = data.uris {
206 for uri in uris {
207 ret.push(Uri(uri));
208 }
209 }
210 if let Some(name) = data.broadcast_name {
211 ret.push(BroadcastName(name));
212 }
213 ret
214}
215
216fn to_gatt_scan_result(peer: &fidl_le::Peer) -> bt_gatt::central::ScanResult {
217 bt_gatt::central::ScanResult {
218 id: to_gatt_peer_id(&peer.id.unwrap()),
219 connectable: peer.connectable.unwrap_or_default(),
220 name: peer.name.clone().map_or(bt_gatt::central::PeerName::Unknown, |n| {
221 bt_gatt::central::PeerName::CompleteName(n)
222 }),
223 advertised: peer
224 .advertising_data
225 .clone()
226 .map_or(Vec::new(), |d| to_gatt_advertising_data(d)),
227 advertising_sid: peer.advertising_sid,
228 periodic_advertising_interval: peer.periodic_advertising_interval,
229 }
230}
231
232fn to_gatt_handle(handle: &fidl_gatt2::Handle) -> bt_gatt::types::Handle {
233 bt_gatt::types::Handle(handle.value)
234}
235
236fn to_fidl_handle(handle: &bt_gatt::types::Handle) -> fidl_gatt2::Handle {
237 fidl_gatt2::Handle { value: handle.0 }
238}
239
240static CCC_UUID_U16: u16 = 0x2902;
242
243fn to_gatt_descriptor(d: &fidl_gatt2::Descriptor) -> Option<bt_gatt::types::Descriptor> {
244 let uuid = to_gatt_uuid(&d.type_.unwrap());
245 let desc_type = match uuid.to_u16() {
246 Some(x) if x == CCC_UUID_U16 => return None,
248 _ => bt_gatt::types::DescriptorType::Other { uuid },
249 };
250 Some(bt_gatt::types::Descriptor {
251 handle: to_gatt_handle(&d.handle.unwrap()),
252 permissions: bt_gatt::types::AttributePermissions::default(),
253 r#type: desc_type,
254 })
255}
256
257fn to_gatt_characteristic(c: &fidl_gatt2::Characteristic) -> bt_gatt::types::Characteristic {
258 let mut property_bits = Vec::new();
259 let properties = c.properties.unwrap();
260 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::BROADCAST) {
261 property_bits.push(bt_gatt::types::CharacteristicProperty::Broadcast);
262 }
263 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::READ) {
264 property_bits.push(bt_gatt::types::CharacteristicProperty::Read);
265 }
266 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::WRITE) {
267 property_bits.push(bt_gatt::types::CharacteristicProperty::Write);
268 }
269 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::WRITE_WITHOUT_RESPONSE) {
270 property_bits.push(bt_gatt::types::CharacteristicProperty::WriteWithoutResponse);
271 }
272 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::NOTIFY) {
273 property_bits.push(bt_gatt::types::CharacteristicProperty::Notify);
274 }
275 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::INDICATE) {
276 property_bits.push(bt_gatt::types::CharacteristicProperty::Indicate);
277 }
278 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::AUTHENTICATED_SIGNED_WRITES) {
279 property_bits.push(bt_gatt::types::CharacteristicProperty::AuthenticatedSignedWrites);
280 }
281 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::RELIABLE_WRITE) {
282 property_bits.push(bt_gatt::types::CharacteristicProperty::ReliableWrite);
283 }
284 if properties.contains(fidl_gatt2::CharacteristicPropertyBits::WRITABLE_AUXILIARIES) {
285 property_bits.push(bt_gatt::types::CharacteristicProperty::WritableAuxiliaries);
286 }
287 let descriptors = c
288 .descriptors
289 .as_ref()
290 .map_or(Vec::new(), |d| d.iter().filter_map(to_gatt_descriptor).collect());
291 bt_gatt::types::Characteristic {
292 handle: to_gatt_handle(&c.handle.unwrap()),
293 uuid: to_gatt_uuid(&c.type_.unwrap()),
294 properties: bt_gatt::types::CharacteristicProperties(property_bits),
295 permissions: bt_gatt::types::AttributePermissions::default(),
296 descriptors,
297 }
298}
299
300pub enum ScanResultStream {
301 Running {
302 proxy: ScanResultWatcherProxy,
303 active_watch: Option<QueryResponseFut<Vec<fidl_le::Peer>>>,
304 queued: Vec<fidl_le::Peer>,
305 _complete_fut: QueryResponseFut<()>,
307 },
308 Terminated,
309}
310
311impl ScanResultStream {
312 fn new(proxy: ScanResultWatcherProxy, complete_fut: QueryResponseFut<()>) -> Self {
313 Self::Running { proxy, _complete_fut: complete_fut, active_watch: None, queued: Vec::new() }
314 }
315}
316
317impl FusedStream for ScanResultStream {
318 fn is_terminated(&self) -> bool {
319 matches!(self, Self::Terminated)
320 }
321}
322
323impl Stream for ScanResultStream {
324 type Item = bt_gatt::Result<bt_gatt::central::ScanResult>;
325
326 fn poll_next(
327 self: Pin<&mut Self>,
328 cx: &mut std::task::Context<'_>,
329 ) -> Poll<Option<Self::Item>> {
330 let this = Pin::into_inner(self);
331 if this.is_terminated() {
332 return Poll::Ready(None);
333 }
334 let Self::Running { proxy, _complete_fut, active_watch, queued } = this else {
335 unreachable!()
336 };
337 if active_watch.is_none() {
338 *active_watch = Some(proxy.watch());
339 }
340 loop {
341 if let Some(next) = queued.pop() {
342 return Poll::Ready(Some(Ok(to_gatt_scan_result(&next))));
343 }
344 if let Some(fut) = active_watch {
345 let watch_result = futures::ready!(fut.poll_unpin(cx));
346 let Ok(mut new_peers) = watch_result else {
347 *this = Self::Terminated;
348 return Poll::Ready(Some(Err(types::Error::Other(Box::new(
349 watch_result.unwrap_err(),
350 )))));
351 };
352 queued.append(&mut new_peers);
353 *active_watch = Some(proxy.watch());
354 }
355 }
356 }
357}
358
359enum ReadQueryFut {
360 Char(QueryResponseFut<fidl_gatt2::RemoteServiceReadCharacteristicResult>),
361 Desc(QueryResponseFut<fidl_gatt2::RemoteServiceReadDescriptorResult>),
362}
363
364enum QueryError {
365 Gatt(bt_gatt::types::Error),
366 Fidl(fidl::Error),
367}
368
369impl Future for ReadQueryFut {
370 type Output = std::result::Result<fidl_gatt2::ReadValue, QueryError>;
371
372 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
373 let res = match self.get_mut() {
374 ReadQueryFut::Char(c) => match futures::ready!(c.poll_unpin(cx)) {
375 Ok(Ok(v)) => Ok(v),
376 Ok(Err(e)) => Err(QueryError::Gatt(to_gatt_gatt_error(&e))),
377 Err(fidl_error) => Err(QueryError::Fidl(fidl_error)),
378 },
379 ReadQueryFut::Desc(c) => match futures::ready!(c.poll_unpin(cx)) {
380 Ok(Ok(v)) => Ok(v),
381 Ok(Err(e)) => Err(QueryError::Gatt(to_gatt_gatt_error(&e))),
382 Err(fidl_error) => Err(QueryError::Fidl(fidl_error)),
383 },
384 };
385 Poll::Ready(res)
386 }
387}
388
389pub struct ReadFuture<'a> {
390 peer_id: bt_common::PeerId,
391 read_fut: ReadQueryFut,
392 target: &'a mut [u8],
393}
394
395impl Future for ReadFuture<'_> {
396 type Output = Result<(usize, bool)>;
397
398 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
399 match futures::ready!(self.read_fut.poll_unpin(cx)) {
400 Ok(fidl_gatt2::ReadValue { value, maybe_truncated, .. }) => {
401 let value = value.unwrap();
402 self.target[..value.len()].copy_from_slice(value.as_slice());
403 Poll::Ready(Ok((value.len(), maybe_truncated.unwrap())))
404 }
405 Err(QueryError::Gatt(e)) => Poll::Ready(Err(e)),
406 Err(QueryError::Fidl(fidl_error)) => {
407 if fidl_error.is_closed() {
408 Poll::Ready(Err(bt_gatt::types::Error::PeerDisconnected(self.peer_id)))
409 } else {
410 Poll::Ready(Err(bt_gatt::types::Error::Other(Box::new(fidl_error))))
411 }
412 }
413 }
414 }
415}
416
417enum WriteQueryFut {
418 Char(QueryResponseFut<fidl_gatt2::RemoteServiceWriteCharacteristicResult>),
419 Desc(QueryResponseFut<fidl_gatt2::RemoteServiceWriteDescriptorResult>),
420}
421
422impl Future for WriteQueryFut {
423 type Output = std::result::Result<(), QueryError>;
424
425 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
426 let res = match self.get_mut() {
427 WriteQueryFut::Char(c) => match futures::ready!(c.poll_unpin(cx)) {
428 Ok(Ok(())) => Ok(()),
429 Ok(Err(e)) => Err(QueryError::Gatt(to_gatt_gatt_error(&e))),
430 Err(fidl_error) => Err(QueryError::Fidl(fidl_error)),
431 },
432 WriteQueryFut::Desc(c) => match futures::ready!(c.poll_unpin(cx)) {
433 Ok(Ok(())) => Ok(()),
434 Ok(Err(e)) => Err(QueryError::Gatt(to_gatt_gatt_error(&e))),
435 Err(fidl_error) => Err(QueryError::Fidl(fidl_error)),
436 },
437 };
438 Poll::Ready(res)
439 }
440}
441
442pub struct WriteFuture<'a> {
443 peer_id: bt_common::PeerId,
444 write_fut: WriteQueryFut,
445 _lifetime: std::marker::PhantomData<&'a ()>,
446}
447
448impl WriteFuture<'_> {
449 fn new(peer_id: bt_common::PeerId, write_fut: WriteQueryFut) -> Self {
450 Self { peer_id, write_fut, _lifetime: std::marker::PhantomData }
451 }
452}
453
454impl Future for WriteFuture<'_> {
455 type Output = Result<()>;
456
457 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
458 match futures::ready!(self.write_fut.poll_unpin(cx)) {
459 Ok(()) => Poll::Ready(Ok(())),
460 Err(QueryError::Gatt(error)) => Poll::Ready(Err(error)),
461 Err(QueryError::Fidl(fidl_error)) => {
462 if fidl_error.is_closed() {
463 Poll::Ready(Err(bt_gatt::types::Error::PeerDisconnected(self.peer_id)))
464 } else {
465 Poll::Ready(Err(bt_gatt::types::Error::Other(Box::new(fidl_error))))
466 }
467 }
468 }
469 }
470}
471
472pub struct CharacteristicNotificationStream {
473 peer_id: bt_common::PeerId,
474 error: Option<bt_gatt::types::Error>,
475 stream: Option<fidl_gatt2::CharacteristicNotifierRequestStream>,
476 result: Option<QueryResponseFut<fidl_gatt2::RemoteServiceRegisterCharacteristicNotifierResult>>,
477}
478
479impl Stream for CharacteristicNotificationStream {
480 type Item = Result<client::CharacteristicNotification>;
481
482 fn poll_next(
483 self: Pin<&mut Self>,
484 cx: &mut std::task::Context<'_>,
485 ) -> Poll<Option<Self::Item>> {
486 let Self { error, stream, peer_id, result } = self.get_mut();
487 loop {
488 if let Some(error) = error.take() {
489 return Poll::Ready(Some(Err(error)));
490 }
491 if let Some(result_fut) = result {
492 if let Poll::Ready(maybe_error) = result_fut.poll_unpin(cx) {
493 *result = None;
494 match maybe_error {
495 Ok(Ok(())) => {}
496 Ok(Err(gatt_error)) => {
497 *error = Some(to_gatt_gatt_error(&gatt_error));
498 continue;
499 }
500 Err(fidl_error) => {
501 *error = Some(bt_gatt::types::Error::Other(Box::new(fidl_error)));
502 continue;
503 }
504 }
505 }
506 }
507 if let Some(next) = stream.as_mut() {
508 let next = futures::ready!(next.poll_next_unpin(cx));
509 let res = match next {
510 Some(Ok(fidl_gatt2::CharacteristicNotifierRequest::OnNotification {
511 value,
512 responder,
513 })) => {
514 let _ = responder.send();
515 Some(Ok(client::CharacteristicNotification {
516 handle: to_gatt_handle(&value.handle.unwrap()),
517 value: value.value.unwrap(),
518 maybe_truncated: value.maybe_truncated.unwrap(),
519 }))
520 }
521 Some(Err(fidl_error)) => {
522 *stream = None;
523 *error = Some(if fidl_error.is_closed() {
524 bt_gatt::types::Error::PeerDisconnected(*peer_id)
525 } else {
526 bt_gatt::types::Error::Other(Box::new(fidl_error))
527 });
528 continue;
529 }
530 None => {
531 *stream = None;
532 None
533 }
534 };
535 return Poll::Ready(res);
536 }
537 panic!("Polled while is_terminated");
538 }
539 }
540}
541
542pub struct CharacteristicResultFut {
543 get_characteristics_fut: QueryResponseFut<Vec<fidl_gatt2::Characteristic>>,
544 filter_uuid: Option<Uuid>,
545}
546
547impl Future for CharacteristicResultFut {
548 type Output = Result<Vec<types::Characteristic>>;
549
550 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
551 let this = self.get_mut();
552 let result = futures::ready!(this.get_characteristics_fut.poll_unpin(cx));
553 let Ok(vec) = result else {
554 return Poll::Ready(Err(types::Error::Other(Box::new(result.unwrap_err()))));
555 };
556 let chars = vec.iter().map(to_gatt_characteristic);
557 let chars = if let Some(uuid) = this.filter_uuid {
558 chars.filter(|c| c.uuid == uuid).collect()
559 } else {
560 chars.collect()
561 };
562 Poll::Ready(Ok(chars))
564 }
565}
566
567pub struct PeerService {
568 peer_id: bt_common::PeerId,
569 proxy: fidl_gatt2::RemoteServiceProxy,
570}
571
572impl bt_gatt::client::PeerService<FuchsiaTypes> for PeerService {
573 fn discover_characteristics(&self, uuid: Option<Uuid>) -> CharacteristicResultFut {
574 let get_characteristics_fut = self.proxy.discover_characteristics();
575 CharacteristicResultFut { get_characteristics_fut, filter_uuid: uuid }
576 }
577
578 fn read_characteristic<'a>(
579 &self,
580 handle: &types::Handle,
581 offset: u16,
582 buf: &'a mut [u8],
583 ) -> <FuchsiaTypes as GattTypes>::ReadFut<'a> {
584 let max_bytes = buf.len().try_into().unwrap_or(u16::MAX);
585 let read_fut = self.proxy.read_characteristic(
586 &to_fidl_handle(handle),
587 &fidl_gatt2::ReadOptions::LongRead(fidl_gatt2::LongReadOptions {
588 offset: Some(offset),
589 max_bytes: Some(max_bytes),
590 ..Default::default()
591 }),
592 );
593 ReadFuture { peer_id: self.peer_id, read_fut: ReadQueryFut::Char(read_fut), target: buf }
594 }
595
596 fn write_characteristic<'a>(
597 &self,
598 handle: &types::Handle,
599 mode: types::WriteMode,
600 offset: u16,
601 buf: &'a [u8],
602 ) -> <FuchsiaTypes as GattTypes>::WriteFut<'a> {
603 let write_fut = self.proxy.write_characteristic(
604 &to_fidl_handle(handle),
605 buf,
606 &fidl_gatt2::WriteOptions {
607 write_mode: Some(to_fidl_writemode(&mode)),
608 offset: Some(offset),
609 ..Default::default()
610 },
611 );
612 WriteFuture::new(self.peer_id, WriteQueryFut::Char(write_fut))
613 }
614
615 fn read_descriptor<'a>(
616 &self,
617 handle: &types::Handle,
618 offset: u16,
619 buf: &'a mut [u8],
620 ) -> <FuchsiaTypes as GattTypes>::ReadFut<'a> {
621 let max_bytes = buf.len().try_into().unwrap_or(u16::MAX);
622 let read_fut = self.proxy.read_descriptor(
623 &to_fidl_handle(handle),
624 &fidl_gatt2::ReadOptions::LongRead(fidl_gatt2::LongReadOptions {
625 offset: Some(offset),
626 max_bytes: Some(max_bytes),
627 ..Default::default()
628 }),
629 );
630 ReadFuture { peer_id: self.peer_id, read_fut: ReadQueryFut::Desc(read_fut), target: buf }
631 }
632
633 fn write_descriptor<'a>(
634 &self,
635 handle: &types::Handle,
636 offset: u16,
637 buf: &'a [u8],
638 ) -> <FuchsiaTypes as GattTypes>::WriteFut<'a> {
639 let write_fut = self.proxy.write_descriptor(
640 &to_fidl_handle(handle),
641 buf,
642 &fidl_gatt2::WriteOptions { offset: Some(offset), ..Default::default() },
643 );
644 WriteFuture::new(self.peer_id, WriteQueryFut::Desc(write_fut))
645 }
646
647 fn subscribe(&self, handle: &types::Handle) -> <FuchsiaTypes as GattTypes>::NotificationStream {
648 let (client, stream) =
649 fidl::endpoints::create_request_stream::<fidl_gatt2::CharacteristicNotifierMarker>();
650 let notifier_fut =
651 self.proxy.register_characteristic_notifier(&to_fidl_handle(handle), client);
652 CharacteristicNotificationStream {
653 peer_id: self.peer_id,
654 error: None,
655 stream: Some(stream),
656 result: Some(notifier_fut),
657 }
658 }
659}
660
661pub struct PeerServiceHandle {
662 peer_id: bt_common::PeerId,
663 uuid: Uuid,
664 service_info: fidl_gatt2::ServiceInfo,
665 handle: fidl_gatt2::ServiceHandle,
666 proxy: fidl_gatt2::ClientProxy,
667}
668
669impl bt_gatt::client::PeerServiceHandle<FuchsiaTypes> for PeerServiceHandle {
670 fn uuid(&self) -> Uuid {
671 self.uuid
672 }
673
674 fn is_primary(&self) -> bool {
675 self.service_info.kind.map_or(false, |k| k == fidl_gatt2::ServiceKind::Primary)
676 }
677
678 fn connect(&self) -> <FuchsiaTypes as GattTypes>::ServiceConnectFut {
679 let (proxy, server_end) =
680 fidl::endpoints::create_proxy::<fidl_gatt2::RemoteServiceMarker>();
681 if let Err(e) = self.proxy.connect_to_service(&self.handle, server_end) {
682 return futures::future::ready(Err(types::Error::Other(Box::new(e))));
683 }
684 futures::future::ready(Ok(PeerService { peer_id: self.peer_id, proxy }))
685 }
686}
687
688#[derive(Clone)]
689pub struct Client {
690 peer_id: PeerId,
691 _connection_proxy: fidl_le::ConnectionProxy,
692 client_proxy: fidl_gatt2::ClientProxy,
693 watched_uuid: Arc<Mutex<Option<fidl_bt::Uuid>>>,
694 known_services: Arc<Mutex<HashMap<u64, fidl_gatt2::ServiceInfo>>>,
695}
696
697impl Client {
698 fn new(
699 peer_id: PeerId,
700 connection_proxy: ConnectionProxy,
701 client_proxy: fidl_gatt2::ClientProxy,
702 ) -> Self {
703 Client {
704 peer_id,
705 _connection_proxy: connection_proxy,
706 client_proxy,
707 watched_uuid: Default::default(),
708 known_services: Default::default(),
709 }
710 }
711}
712
713const SERVICE_UPDATE_TIMEOUT: fasync::MonotonicDuration =
715 fasync::MonotonicDuration::from_seconds(3);
716
717impl bt_gatt::Client<FuchsiaTypes> for Client {
718 fn peer_id(&self) -> PeerId {
719 self.peer_id
720 }
721
722 fn find_service(&self, uuid: Uuid) -> <FuchsiaTypes as GattTypes>::FindServicesFut {
723 let fidl_uuid = to_fidl_uuid(&uuid);
724 fasync::Task::spawn({
725 let watched_uuid = self.watched_uuid.clone();
726 let known_services = self.known_services.clone();
727 let client_proxy = self.client_proxy.clone();
728 let peer_id = self.peer_id;
729 let timeout = fasync::MonotonicInstant::after(SERVICE_UPDATE_TIMEOUT);
730 async move {
731 let result = client_proxy
732 .watch_services(&[fidl_uuid])
733 .on_timeout(timeout, || Ok((Vec::new(), Vec::new())))
734 .await;
735 let Ok((added, removed)) = result else {
736 return Err(types::Error::Other(Box::new(result.unwrap_err())));
737 };
738 let mut watched_uuid = watched_uuid.lock();
739 let mut known_services = known_services.lock();
740 match *watched_uuid {
741 Some(current) if current == fidl_uuid => {
742 removed
743 .into_iter()
744 .for_each(|handle| drop(known_services.remove(&handle.value)));
745 }
746 _ => {
747 known_services.clear();
748 *watched_uuid = Some(fidl_uuid);
749 }
750 };
751 for info in added {
752 let _ = known_services.insert(info.handle.unwrap().value, info);
754 }
755 let services = known_services
756 .iter()
757 .map(|(handle, service_info)| PeerServiceHandle {
758 peer_id,
759 uuid,
760 service_info: service_info.clone(),
761 handle: fidl_gatt2::ServiceHandle { value: *handle },
762 proxy: client_proxy.clone(),
763 })
764 .collect();
765 Ok(services)
766 }
767 })
768 }
769}
770
771pub struct Server {
772 proxy: fidl_fuchsia_bluetooth_gatt2::Server_Proxy,
773}
774
775impl Server {
776 pub fn new(proxy: fidl_gatt2::Server_Proxy) -> Self {
777 Self { proxy }
778 }
779}
780
781fn to_fidl_desc(gatt: &bt_gatt::types::Descriptor) -> fidl_gatt2::Descriptor {
782 fidl_gatt2::Descriptor {
783 handle: Some(to_fidl_handle(&gatt.handle)),
784 type_: Some(to_fidl_uuid(&(&gatt.r#type).into())),
785 permissions: Some(to_fidl_permissions(&gatt.permissions)),
786 ..Default::default()
787 }
788}
789
790fn to_fidl_levels(gatt: &bt_gatt::types::SecurityLevels) -> fidl_gatt2::SecurityRequirements {
791 fidl_gatt2::SecurityRequirements {
792 encryption_required: Some(gatt.encryption),
793 authentication_required: Some(gatt.authentication),
794 authorization_required: Some(gatt.authorization),
795 ..Default::default()
796 }
797}
798
799fn to_fidl_permissions(
800 gatt: &bt_gatt::types::AttributePermissions,
801) -> fidl_gatt2::AttributePermissions {
802 fidl_gatt2::AttributePermissions {
803 read: gatt.read.as_ref().map(to_fidl_levels),
804 write: gatt.write.as_ref().map(to_fidl_levels),
805 update: gatt.update.as_ref().map(to_fidl_levels),
806 ..Default::default()
807 }
808}
809
810fn to_fidl_char(gatt: &bt_gatt::types::Characteristic) -> fidl_gatt2::Characteristic {
811 let properties = fidl_gatt2::CharacteristicPropertyBits::from_bits(
813 gatt.properties.0.iter().fold(0, |acc, prop| acc | (*prop as u16)),
814 );
815 fidl_gatt2::Characteristic {
816 handle: Some(to_fidl_handle(&gatt.handle)),
817 type_: Some(to_fidl_uuid(&gatt.uuid)),
818 properties,
819 permissions: Some(to_fidl_permissions(&gatt.permissions)),
820 descriptors: Some(gatt.descriptors().map(to_fidl_desc).collect()),
821 ..Default::default()
822 }
823}
824
825fn from_gatt_service_definition(gatt_def: server::ServiceDefinition) -> fidl_gatt2::ServiceInfo {
826 let mut res = fidl_gatt2::ServiceInfo::default();
827 let service_id: u64 = gatt_def.id().into();
828 res.handle = Some(fidl_gatt2::ServiceHandle { value: service_id });
829 let kind = match gatt_def.kind() {
830 bt_gatt::types::ServiceKind::Primary => fidl_gatt2::ServiceKind::Primary,
831 bt_gatt::types::ServiceKind::Secondary => fidl_gatt2::ServiceKind::Secondary,
832 };
833 res.kind = Some(kind);
834 res.type_ = Some(to_fidl_uuid(&gatt_def.uuid()));
835 res.characteristics = Some(gatt_def.characteristics().map(to_fidl_char).collect());
836 res
837}
838
839impl bt_gatt::Server<FuchsiaTypes> for Server {
840 fn prepare(
841 &self,
842 service: server::ServiceDefinition,
843 ) -> <FuchsiaTypes as ServerTypes>::LocalServiceFut {
844 let info = from_gatt_service_definition(service);
845 let (client, request_stream) = fidl::endpoints::create_request_stream::<
846 fidl_fuchsia_bluetooth_gatt2::LocalServiceMarker,
847 >();
848 LocalServiceFut {
849 future: self.proxy.publish_service(&info, client),
850 request_stream: Some(request_stream),
851 }
852 }
853}
854
855pub struct LocalServiceFut {
856 future: QueryResponseFut<ServerPublishServiceResult>,
857 request_stream: Option<LocalServiceRequestStream>,
858}
859
860impl Future for LocalServiceFut {
861 type Output = Result<LocalService>;
862
863 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
864 let result = futures::ready!(self.future.poll_unpin(cx));
865 let stream = self.request_stream.take().expect("polled after terminated");
866 match result {
867 Ok(Ok(())) => Poll::Ready(Ok(stream.into())),
868 Ok(Err(e)) => {
869 use bt_gatt::types::Error;
870 use fidl_fuchsia_bluetooth_gatt2::PublishServiceError::*;
871 let gatt_err = match e {
872 InvalidServiceHandle => Error::from("Invalid service handle"),
873 InvalidUuid => Error::from("Invalid UUID"),
874 InvalidCharacteristics => Error::from("Invalid Characteristics"),
875 _ => Error::from("Sapphire stack error"),
876 };
877 Poll::Ready(Err(gatt_err))
878 }
879 Err(fidl_err) => Poll::Ready(Err(bt_gatt::types::Error::other(fidl_err))),
880 }
881 }
882}
883
884impl FusedFuture for LocalServiceFut {
885 fn is_terminated(&self) -> bool {
886 self.request_stream.is_none()
887 }
888}
889
890enum WaitingSendItem {
891 Notification(ValueChangedParameters),
892 Indication(ValueChangedParameters, EventPair),
893}
894
895struct ServiceSender {
896 credits: Arc<Mutex<u32>>,
897 waiting: Arc<Mutex<VecDeque<WaitingSendItem>>>,
898 control_handle: LocalServiceControlHandle,
899}
900
901impl ServiceSender {
902 fn new(control_handle: LocalServiceControlHandle) -> Self {
903 Self {
904 credits: Arc::new(Mutex::new(fidl_gatt2::INITIAL_VALUE_CHANGED_CREDITS)),
905 waiting: Default::default(),
906 control_handle,
907 }
908 }
909
910 fn defunct() -> Self {
911 let (_, closed) = zx::Channel::create();
912 let dead = fidl_gatt2::LocalServiceRequestStream::from_channel(
913 fasync::Channel::from_channel(closed),
914 );
915 Self::new(dead.control_handle())
916 }
917
918 fn add_notification(&self, params: ValueChangedParameters) {
919 self.waiting.lock().push_back(WaitingSendItem::Notification(params));
920 self.try_send();
921 }
922
923 fn add_indication(&self, params: ValueChangedParameters, pair: EventPair) {
924 self.waiting.lock().push_back(WaitingSendItem::Indication(params, pair));
925 self.try_send();
926 }
927
928 fn add_credits(&self, additional: u32) {
929 *self.credits.lock() += additional;
930 self.try_send();
931 }
932
933 fn try_send(&self) {
934 let mut credits_lock = self.credits.lock();
935 loop {
936 if *credits_lock == 0 {
937 return;
938 }
939 let mut waiting_lock = self.waiting.lock();
940 let Some(next) = waiting_lock.pop_front() else {
941 return;
942 };
943 *credits_lock -= 1;
944 let res = match next {
945 WaitingSendItem::Notification(params) => {
946 self.control_handle.send_on_notify_value(¶ms)
947 }
948 WaitingSendItem::Indication(params, pair) => {
949 self.control_handle.send_on_indicate_value(¶ms, pair)
950 }
951 };
952 if res.is_err() {
953 return;
954 }
955 }
956 }
957}
958
959pub struct LocalService {
960 stream: Mutex<Option<LocalServiceRequestStream>>,
962 sender: Arc<ServiceSender>,
963}
964
965impl From<LocalServiceRequestStream> for LocalService {
966 fn from(value: LocalServiceRequestStream) -> Self {
967 let sender = Arc::new(ServiceSender::new(value.control_handle()));
968 Self { stream: Mutex::new(Some(value)), sender }
969 }
970}
971
972impl bt_gatt::server::LocalService<FuchsiaTypes> for LocalService {
973 fn publish(&self) -> <FuchsiaTypes as ServerTypes>::ServiceEventStream {
974 match self.stream.lock().take() {
975 None => LocalEventStream::error(bt_gatt::types::Error::from("already published")),
976 Some(stream) => LocalEventStream::new(stream, self.sender.clone()),
977 }
978 }
979
980 fn notify(&self, characteristic: &types::Handle, data: &[u8], peers: &[PeerId]) {
981 self.sender.add_notification(ValueChangedParameters {
982 handle: Some(to_fidl_handle(characteristic)),
983 value: Some(data.into()),
984 peer_ids: Some(peers.iter().map(to_fidl_peer_id).collect()),
985 ..Default::default()
986 });
987 }
988
989 fn indicate(
990 &self,
991 characteristic: &types::Handle,
992 data: &[u8],
993 peers: &[PeerId],
994 ) -> <FuchsiaTypes as ServerTypes>::IndicateConfirmationStream {
995 let (indication_stream, their_pair) = IndicateConfirmationStream::new(peers.into());
996
997 self.sender.add_indication(
998 ValueChangedParameters {
999 handle: Some(to_fidl_handle(characteristic)),
1000 value: Some(data.into()),
1001 peer_ids: Some(peers.iter().map(to_fidl_peer_id).collect()),
1002 ..Default::default()
1003 },
1004 their_pair,
1005 );
1006 indication_stream
1007 }
1008}
1009
1010pub struct LocalEventStream {
1011 stream: Option<Result<LocalServiceRequestStream>>,
1012 sender: Arc<ServiceSender>,
1014}
1015
1016impl LocalEventStream {
1017 fn error(error: bt_gatt::types::Error) -> Self {
1019 Self { stream: Some(Err(error)), sender: Arc::new(ServiceSender::defunct()) }
1020 }
1021
1022 fn new(stream: LocalServiceRequestStream, sender: Arc<ServiceSender>) -> Self {
1023 Self { stream: Some(Ok(stream)), sender }
1024 }
1025}
1026
1027impl Stream for LocalEventStream {
1028 type Item = Result<server::ServiceEvent<FuchsiaTypes>>;
1029
1030 fn poll_next(
1031 mut self: Pin<&mut Self>,
1032 cx: &mut std::task::Context<'_>,
1033 ) -> Poll<Option<Self::Item>> {
1034 let sender = self.sender.clone();
1035 let Some(result) = self.stream.as_mut() else {
1036 return Poll::Ready(None);
1037 };
1038 let Ok(stream) = result.as_mut() else {
1039 let result = self.stream.take();
1040 return Poll::Ready(Some(Err(result.unwrap().err().unwrap())));
1041 };
1042 loop {
1043 let Some(res) = futures::ready!(stream.poll_next_unpin(cx)) else {
1044 self.stream = None;
1045 return Poll::Ready(None);
1046 };
1047 let Ok(request) = res else {
1048 self.stream = None;
1049 return Poll::Ready(Some(Err(bt_gatt::types::Error::other(res.unwrap_err()))));
1050 };
1051 use bt_gatt::server::ServiceEvent;
1052 use fidl_fuchsia_bluetooth_gatt2::LocalServiceRequest::*;
1053 use fidl_fuchsia_bluetooth_gatt2::{
1054 LocalServicePeerUpdateRequest, LocalServiceWriteValueRequest,
1055 };
1056 match request {
1057 CharacteristicConfiguration { peer_id, handle, notify, indicate, responder } => {
1058 let indicate_type = match (notify, indicate) {
1059 (_, true) => bt_gatt::server::NotificationType::Indicate,
1060 (true, false) => bt_gatt::server::NotificationType::Notify,
1061 (false, false) => bt_gatt::server::NotificationType::Disable,
1062 };
1063 let _ = responder.send();
1064 return Poll::Ready(Some(Ok(ServiceEvent::ClientConfiguration {
1065 peer_id: to_gatt_peer_id(&peer_id),
1066 handle: to_gatt_handle(&handle),
1067 notification_type: indicate_type,
1068 })));
1069 }
1070 ReadValue { peer_id, handle, offset, responder } => {
1071 let responder = ReadResponder { responder };
1072 return Poll::Ready(Some(Ok(ServiceEvent::Read {
1073 peer_id: to_gatt_peer_id(&peer_id),
1074 handle: to_gatt_handle(&handle),
1075 offset: offset.try_into().unwrap(),
1076 responder,
1077 })));
1078 }
1079 WriteValue {
1080 payload: LocalServiceWriteValueRequest { peer_id, handle, offset, value, .. },
1081 responder,
1082 } => {
1083 let responder = WriteResponder { responder };
1084 return Poll::Ready(Some(Ok(ServiceEvent::Write {
1085 peer_id: to_gatt_peer_id(&peer_id.unwrap()),
1086 handle: to_gatt_handle(&handle.unwrap()),
1087 offset: offset.unwrap().try_into().unwrap(),
1088 value: value.unwrap(),
1089 responder,
1090 })));
1091 }
1092 PeerUpdate {
1093 payload: LocalServicePeerUpdateRequest { peer_id, mtu, .. },
1094 responder,
1095 } => {
1096 let _ = responder.send();
1097 return Poll::Ready(Some(Ok(ServiceEvent::peer_info(
1098 to_gatt_peer_id(&peer_id.unwrap()),
1099 mtu,
1100 None,
1101 ))));
1102 }
1103 ValueChangedCredit { additional_credit, control_handle: _ } => {
1104 sender.add_credits(additional_credit as u32);
1105 }
1106 }
1107 }
1108 }
1109}
1110
1111pub struct IndicateConfirmationStream {
1112 event: Option<Pin<Box<dyn Future<Output = std::result::Result<zx::Signals, zx::Status>>>>>,
1113 peers: Vec<PeerId>,
1114}
1115
1116impl IndicateConfirmationStream {
1117 fn new(peers: Vec<PeerId>) -> (Self, EventPair) {
1118 let (ours, theirs) = fidl::EventPair::create();
1119 let signals = fuchsia_async::OnSignals::new(
1120 ours,
1121 zx::Signals::EVENTPAIR_SIGNALED | zx::Signals::EVENTPAIR_PEER_CLOSED,
1122 );
1123 (Self { event: Some(Box::pin(signals)), peers }, theirs)
1124 }
1125}
1126
1127impl Stream for IndicateConfirmationStream {
1128 type Item = Result<bt_gatt::server::ConfirmationEvent>;
1129
1130 fn poll_next(
1131 mut self: Pin<&mut Self>,
1132 cx: &mut std::task::Context<'_>,
1133 ) -> Poll<Option<Self::Item>> {
1134 loop {
1135 let Some(signals) = self.event.as_mut() else {
1136 match self.peers.pop() {
1137 None => return Poll::Ready(None),
1138 Some(peer_id) => {
1139 return Poll::Ready(Some(Ok(
1140 bt_gatt::server::ConfirmationEvent::create_ack(peer_id),
1141 )));
1142 }
1143 }
1144 };
1145 let signal = futures::ready!(signals.as_mut().poll(cx));
1146 self.event = None;
1147 use bt_gatt::types::Error;
1148 match signal {
1149 Ok(zx::Signals::EVENTPAIR_SIGNALED) => continue,
1151 Ok(zx::Signals::EVENTPAIR_PEER_CLOSED) => {
1152 self.peers.clear();
1153 return Poll::Ready(Some(Err(Error::from("Peer not subscribed or timed out"))));
1154 }
1155 Ok(sig) => {
1156 self.peers.clear();
1157 return Poll::Ready(Some(Err(Error::from(format!(
1158 "Unexpected signal: {sig:?}",
1159 )))));
1160 }
1161 Err(e) => {
1162 self.peers.clear();
1163 return Poll::Ready(Some(Err(Error::from(format!(
1164 "Error on pair wait: {e:?}",
1165 )))));
1166 }
1167 }
1168 }
1169 }
1170}
1171
1172pub struct ReadResponder {
1173 responder: fidl_gatt2::LocalServiceReadValueResponder,
1174}
1175
1176impl server::ReadResponder for ReadResponder {
1177 fn respond(self, value: &[u8]) {
1178 let _ = self.responder.send(Ok(value.into()));
1179 }
1180
1181 fn error(self, error: types::GattError) {
1182 let _ = self.responder.send(Err(to_fidl_gatt_error(&error)));
1183 }
1184}
1185
1186pub struct WriteResponder {
1187 responder: fidl_gatt2::LocalServiceWriteValueResponder,
1188}
1189
1190impl server::WriteResponder for WriteResponder {
1191 fn acknowledge(self) {
1192 let _ = self.responder.send(Ok(()));
1193 }
1194
1195 fn error(self, error: types::GattError) {
1196 let _ = self.responder.send(Err(to_fidl_gatt_error(&error)));
1197 }
1198}