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