1use 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: ¢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 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
249static 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 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 _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 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
722const 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 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 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(¶ms)
956 }
957 WaitingSendItem::Indication(params, pair) => {
958 self.control_handle.send_on_indicate_value(¶ms, pair)
959 }
960 };
961 if res.is_err() {
962 return;
963 }
964 }
965 }
966}
967
968pub struct LocalService {
969 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 sender: Arc<ServiceSender>,
1023}
1024
1025impl LocalEventStream {
1026 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 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}