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
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: &central::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    // These match up.
163    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
240/// UUID for Client Characteristic Configuration (u16 for matching)
241static 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        // CCC is handled elsewhere
247        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        // TODO: decide if we need to have this complete before we return None from the scan.
306        _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        // TODO: Fetch the well-known Descriptors for these Characteristics.
563        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
713/// Time to wait for services update from a peer on a hanging get.
714const 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                    // updating a known service is okay, new info will be the most up-to-date
753                    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    // Property bits match between bt_gatt and fidl_gatt2
812    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(&params)
947                }
948                WaitingSendItem::Indication(params, pair) => {
949                    self.control_handle.send_on_indicate_value(&params, pair)
950                }
951            };
952            if res.is_err() {
953                return;
954            }
955        }
956    }
957}
958
959pub struct LocalService {
960    // The request stream. None if the service has been published.
961    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    // Used to add credits and send waiting indications.
1013    sender: Arc<ServiceSender>,
1014}
1015
1016impl LocalEventStream {
1017    /// Construct a stream that only contains an error.
1018    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                // Continue to the top of the loop to start draining the ack queue
1150                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}