blob: 8415f691cfda528b34ace07c427e9ff39d57b771 [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::types::*;
use bt_common::{PeerId, Uuid};
use futures::{Future, Stream};
pub enum ServiceKind {
Primary,
Secondary,
}
// A short definition of a service that is either being published or has been discovered on a
// peer.
pub struct PeerServiceDefinition {
/// Service Handle
/// When publishing services, unique among all services published with [`Server::publish`].
pub id: u64,
/// Whether the service is marked as Primary in the GATT server.
pub kind: ServiceKind,
/// The UUID identifying the type of service that this is.
pub uuid: Uuid,
}
/// GATT Client connected to a particular peer.
/// Holding a struct that implements this should attempt to maintain a LE connection to the peer.
pub trait Client {
type PeerServiceHandleT: PeerServiceHandle;
type ServiceResultFut: Future<Output = Result<Vec<Self::PeerServiceHandleT>>> + 'static;
/// The ID of the peer this is connected to.
fn peer_id(&self) -> PeerId;
/// Find services by UUID on the peer.
/// This may cause as much as a full discovery of all services on the peer if the stack deems it
/// appropriate.
/// Service information should be up to date at the time returned.
fn find_service(&self, uuid: Uuid) -> Self::ServiceResultFut;
}
pub trait PeerServiceHandle {
type PeerServiceT: PeerService;
type ConnectFut: Future<Output = Result<Self::PeerServiceT>>;
fn uuid(&self) -> Uuid;
fn is_primary(&self) -> bool;
fn connect(&self) -> Self::ConnectFut;
}
#[derive(Debug)]
pub struct CharacteristicNotification {
pub handle: Handle,
pub value: Vec<u8>,
pub maybe_truncated: bool,
}
/// A connection to a GATT Service on a Peer.
/// All operations are done synchronously.
pub trait PeerService {
type CharacteristicsFut: Future<Output = Result<Vec<Characteristic>>>;
type NotificationStream: Stream<Item = Result<CharacteristicNotification>>;
type ReadFut<'a>: Future<Output = Result<(usize, bool)>> + 'a;
type WriteFut<'a>: Future<Output = Result<()>> + 'a;
/// Discover characteristics on this service.
/// If `uuid` is provided, only the characteristics matching `uuid` will be returned.
/// This operation may use either the Discovery All Characteristics of a Service or
/// Discovery Characteristic by UUID procedures, regardless of `uuid`.
fn discover_characteristics(&self, uuid: Option<Uuid>) -> Self::CharacteristicsFut;
/// Read a characteristic into a buffer, given the handle within the service.
/// On success, returns the size read and whether the value may have been truncated.
/// By default this will try to use a long read if the `buf` is larger than a normal read will
/// allow (22 bytes) or if the offset is non-zero.
fn read_characteristic<'a>(
&self,
handle: &Handle,
offset: u16,
buf: &'a mut [u8],
) -> Self::ReadFut<'a>;
fn write_characteristic<'a>(
&self,
handle: &Handle,
mode: WriteMode,
offset: u16,
buf: &'a [u8],
) -> Self::WriteFut<'a>;
fn read_descriptor<'a>(
&self,
handle: &Handle,
offset: u16,
buf: &'a mut [u8],
) -> Self::ReadFut<'a>;
fn write_descriptor<'a>(
&self,
handle: &Handle,
offset: u16,
buf: &'a [u8],
) -> Self::WriteFut<'a>;
/// Subscribe to updates on a Characteristic.
/// Either notifications or indications will be enabled depending on the properties available,
/// with indications preferred if they are supported.
/// Fails if the Characteristic doesn't support indications or notifications.
/// Errors are delivered through an Err item in the stream.
/// This will often write to the Client Characteristic Configuration descriptor for the
/// Characteristic subscribed to.
/// Updates sent from the peer wlil be delivered to the Stream returned.
fn subscribe(&self, handle: &Handle) -> Self::NotificationStream;
}
/// Convenience class for communicating with characteristics on a remote peer.
pub struct ServiceCharacteristic<'a, PeerServiceT> {
service: &'a PeerServiceT,
characteristic: Characteristic,
uuid: Uuid,
}
impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> {
pub async fn find(
service: &'a PeerServiceT,
uuid: Uuid,
) -> Result<Vec<ServiceCharacteristic<'a, PeerServiceT>>> {
let chrs = service.discover_characteristics(Some(uuid)).await?;
Ok(chrs
.into_iter()
.map(|characteristic| Self {
service,
characteristic,
uuid,
})
.collect())
}
}
impl<'a, PeerServiceT> ServiceCharacteristic<'a, PeerServiceT> {
pub fn uuid(&self) -> Uuid {
self.uuid
}
pub fn handle(&self) -> &Handle {
&self.characteristic.handle
}
pub fn characteristic(&self) -> &Characteristic {
&self.characteristic
}
}
impl<'a, PeerServiceT: PeerService> ServiceCharacteristic<'a, PeerServiceT> {
pub async fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.service
.read_characteristic(self.handle(), 0, buf)
.await
.map(|(bytes, _)| bytes)
}
}