|  | // Copyright 2023 Google LLC | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | use crate::{central::ScanResult, client::CharacteristicNotification, types::*}; | 
|  |  | 
|  | use bt_common::{PeerId, Uuid}; | 
|  | use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; | 
|  | use futures::{Future, Stream}; | 
|  | use futures::future::{Ready, ready}; | 
|  | use parking_lot::Mutex; | 
|  | use std::collections::HashMap; | 
|  | use std::task::Poll; | 
|  | use std::sync::Arc; | 
|  |  | 
|  | #[derive(Default)] | 
|  | pub(crate) struct FakeCentral {} | 
|  |  | 
|  | #[derive(Default)] | 
|  | struct FakePeerServiceInner { | 
|  | // Notifier that's used to send out notification. | 
|  | notifiers: HashMap<Handle, UnboundedSender<Result<CharacteristicNotification>>>, | 
|  |  | 
|  | // Characteristics to return when `read_characteristic` and `discover_characteristics` are called. | 
|  | characteristics: HashMap<Handle, (Characteristic, Vec<u8>)>, | 
|  | } | 
|  |  | 
|  | #[derive(Clone)] | 
|  | pub struct FakePeerService { | 
|  | inner: Arc<Mutex<FakePeerServiceInner>>, | 
|  | } | 
|  |  | 
|  | impl FakePeerService { | 
|  | pub fn new() -> Self { | 
|  | Self { inner: Arc::new(Mutex::new(Default::default())) } | 
|  | } | 
|  |  | 
|  | // Adds a characteristic so that it can be returned when discover/read method is | 
|  | // called. | 
|  | // Also triggers sending a characteristic value change notification to be sent. | 
|  | pub fn add_characteristic(&mut self, char: Characteristic, value: Vec<u8>) { | 
|  | let mut lock = self.inner.lock(); | 
|  | let handle = char.handle; | 
|  | lock.characteristics.insert(handle, (char, value.clone())); | 
|  | if let Some(notifier) = lock.notifiers.get_mut(&handle) { | 
|  | notifier.unbounded_send(Ok(CharacteristicNotification { handle, value, maybe_truncated: false })).expect("should succeed"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl crate::client::PeerService for FakePeerService { | 
|  | type CharacteristicsFut = Ready<Result<Vec<Characteristic>>>; | 
|  | type NotificationStream = UnboundedReceiver<Result<CharacteristicNotification>>; | 
|  | type ReadFut<'a> = Ready<Result<(usize, bool)>>; | 
|  | type WriteFut<'a> = Ready<Result<()>>; | 
|  |  | 
|  | fn discover_characteristics(&self, uuid: Option<Uuid>) -> Self::CharacteristicsFut { | 
|  | let lock = self.inner.lock(); | 
|  | let mut result = Vec::new(); | 
|  | for (_handle, (char, _value))in &lock.characteristics { | 
|  | match uuid { | 
|  | Some(uuid) if uuid == char.uuid => result.push(char.clone()), | 
|  | None => result.push(char.clone()), | 
|  | _ => {}, | 
|  | } | 
|  | } | 
|  | ready(Ok(result)) | 
|  | } | 
|  |  | 
|  | fn read_characteristic<'a>( | 
|  | &self, | 
|  | handle: &Handle, | 
|  | _offset: u16, | 
|  | buf: &'a mut [u8], | 
|  | ) -> Self::ReadFut<'a> { | 
|  | let read_characteristics = &(*self.inner.lock()).characteristics; | 
|  | let Some((_, value)) = read_characteristics.get(handle) else { | 
|  | return ready(Err(Error::Gatt(GattError::InvalidHandle))); | 
|  | }; | 
|  | buf[..value.len()].copy_from_slice(value.as_slice()); | 
|  | ready(Ok((value.len(), false))) | 
|  | } | 
|  |  | 
|  | fn write_characteristic<'a>( | 
|  | &self, | 
|  | _handle: &Handle, | 
|  | _mode: WriteMode, | 
|  | _offset: u16, | 
|  | _buf: &'a [u8], | 
|  | ) -> Self::WriteFut<'a> { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn read_descriptor<'a>( | 
|  | &self, | 
|  | _handle: &Handle, | 
|  | _offset: u16, | 
|  | _buf: &'a mut [u8], | 
|  | ) -> Self::ReadFut<'a> { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn write_descriptor<'a>( | 
|  | &self, | 
|  | _handle: &Handle, | 
|  | _offset: u16, | 
|  | _buf: &'a [u8], | 
|  | ) -> Self::WriteFut<'a> { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn subscribe(&self, handle: &Handle) -> Self::NotificationStream { | 
|  | let (sender, receiver) = unbounded(); | 
|  | (*self.inner.lock()).notifiers.insert(*handle, sender); | 
|  | receiver | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) struct FakeServiceHandle {} | 
|  |  | 
|  | impl crate::client::PeerServiceHandle for FakeServiceHandle { | 
|  | type PeerServiceT = FakePeerService; | 
|  | type ConnectFut = Ready<Result<Self::PeerServiceT>>; | 
|  |  | 
|  | fn uuid(&self) -> Uuid { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn is_primary(&self) -> bool { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn connect(&self) -> Self::ConnectFut { | 
|  | todo!() | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) struct FakeClient {} | 
|  |  | 
|  | impl crate::Client for FakeClient { | 
|  | type PeerServiceHandleT = FakeServiceHandle; | 
|  | type ServiceResultFut = Ready<Result<Vec<Self::PeerServiceHandleT>>>; | 
|  |  | 
|  | fn peer_id(&self) -> PeerId { | 
|  | todo!() | 
|  | } | 
|  |  | 
|  | fn find_service(&self, _uuid: Uuid) -> Self::ServiceResultFut { | 
|  | todo!() | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) struct SingleResultStream { | 
|  | result: Option<Result<crate::central::ScanResult>>, | 
|  | } | 
|  |  | 
|  | impl Stream for SingleResultStream { | 
|  | type Item = Result<crate::central::ScanResult>; | 
|  |  | 
|  | fn poll_next( | 
|  | self: std::pin::Pin<&mut Self>, | 
|  | _cx: &mut std::task::Context<'_>, | 
|  | ) -> Poll<Option<Self::Item>> { | 
|  | if self.result.is_some() { | 
|  | Poll::Ready(self.get_mut().result.take()) | 
|  | } else { | 
|  | // Never wake up, as if we never find another result | 
|  | Poll::Pending | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(crate) struct ClientConnectFut {} | 
|  |  | 
|  | impl Future for ClientConnectFut { | 
|  | type Output = Result<FakeClient>; | 
|  |  | 
|  | fn poll( | 
|  | self: std::pin::Pin<&mut Self>, | 
|  | _cx: &mut std::task::Context<'_>, | 
|  | ) -> Poll<Self::Output> { | 
|  | todo!() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl crate::Central for FakeCentral { | 
|  | type ScanResultStream = SingleResultStream; | 
|  |  | 
|  | type Client = FakeClient; | 
|  |  | 
|  | type ClientFut = ClientConnectFut; | 
|  |  | 
|  | fn scan(&self, _filters: &[crate::central::ScanFilter]) -> Self::ScanResultStream { | 
|  | SingleResultStream { | 
|  | result: Some(Ok(ScanResult { | 
|  | id: PeerId(1), | 
|  | connectable: true, | 
|  | name: crate::central::PeerName::CompleteName("Marie's Pixel 7 Pro".to_owned()), | 
|  | advertised: vec![crate::central::AdvertisingDatum::Services(vec![ | 
|  | Uuid::from_u16(0x1844), | 
|  | ])], | 
|  | })), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn connect(&self, _peer_id: PeerId) -> Self::ClientFut { | 
|  | todo!() | 
|  | } | 
|  | } |