Skip to main content

bt_gatt_fuchsia/
lib.rs

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