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