blob: 97393d2146479a7e9ca9f83a5890973d4c6041d4 [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, 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!()
}
}