blob: c4189d7a2d559968c4d60815fef7c95627d18ed8 [file] [log] [blame]
// 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;
use crate::client::{self, CharacteristicNotification};
use crate::{types::*, GattTypes};
use bt_common::{PeerId, Uuid};
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::future::{ready, Ready};
use futures::{Future, Stream};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::sync::Arc;
use std::task::Poll;
#[derive(Default)]
pub 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<FakeTypes> for FakePeerService {
fn discover_characteristics(
&self,
uuid: Option<Uuid>,
) -> <FakeTypes as GattTypes>::CharacteristicDiscoveryFut {
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],
) -> <FakeTypes as GattTypes>::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],
) -> <FakeTypes as GattTypes>::WriteFut<'a> {
todo!()
}
fn read_descriptor<'a>(
&self,
_handle: &Handle,
_offset: u16,
_buf: &'a mut [u8],
) -> <FakeTypes as GattTypes>::ReadFut<'a> {
todo!()
}
fn write_descriptor<'a>(
&self,
_handle: &Handle,
_offset: u16,
_buf: &'a [u8],
) -> <FakeTypes as GattTypes>::WriteFut<'a> {
todo!()
}
fn subscribe(&self, handle: &Handle) -> <FakeTypes as GattTypes>::NotificationStream {
let (sender, receiver) = unbounded();
(*self.inner.lock()).notifiers.insert(*handle, sender);
receiver
}
}
pub struct FakeServiceHandle {}
impl crate::client::PeerServiceHandle<FakeTypes> for FakeServiceHandle {
fn uuid(&self) -> Uuid {
todo!()
}
fn is_primary(&self) -> bool {
todo!()
}
fn connect(&self) -> <FakeTypes as GattTypes>::ServiceConnectFut {
futures::future::ready(Ok(FakePeerService::new()))
}
}
pub struct FakeClient {}
impl crate::Client<FakeTypes> for FakeClient {
fn peer_id(&self) -> PeerId {
todo!()
}
fn find_service(&self, _uuid: Uuid) -> <FakeTypes as GattTypes>::FindServicesFut {
futures::future::ready(Ok::<Vec<FakeServiceHandle>, Error>(vec![FakeServiceHandle {}]))
}
}
pub 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!()
}
}
pub struct FakeTypes {}
impl GattTypes for FakeTypes {
type Central = FakeCentral;
type ScanResultStream = SingleResultStream;
type Client = FakeClient;
type ConnectFuture = Ready<Result<FakeClient>>;
type PeerServiceHandle = FakeServiceHandle;
type FindServicesFut = Ready<Result<Vec<FakeServiceHandle>>>;
type PeerService = FakePeerService;
type ServiceConnectFut = Ready<Result<FakePeerService>>;
type CharacteristicDiscoveryFut = Ready<Result<Vec<Characteristic>>>;
type NotificationStream = UnboundedReceiver<Result<client::CharacteristicNotification>>;
type ReadFut<'a> = Ready<Result<(usize, bool)>>;
type WriteFut<'a> = Ready<Result<()>>;
}
impl crate::Central<FakeTypes> for FakeCentral {
fn scan(&self, _filters: &[crate::central::ScanFilter]) -> SingleResultStream {
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) -> <FakeTypes as GattTypes>::ConnectFuture {
futures::future::ready(Ok(FakeClient {}))
}
}