bt_gatt/
server.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::HashSet;
6
7use bt_common::{PeerId, Uuid};
8
9use crate::types::*;
10
11/// ServiceId is used to identify a local service when publishing.
12#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
13pub struct ServiceId(u64);
14
15impl ServiceId {
16    pub const fn new(id: u64) -> Self {
17        Self(id)
18    }
19}
20
21impl From<ServiceId> for u64 {
22    fn from(value: ServiceId) -> Self {
23        value.0
24    }
25}
26
27/// Defines a service to be added to a Server
28#[derive(Debug, Clone)]
29pub struct ServiceDefinition {
30    /// Local identifier used to include services within each other.
31    id: ServiceId,
32    /// UUID identifying the type of service
33    uuid: Uuid,
34    /// Whether the service is marked as Primary in the GATT server.
35    kind: ServiceKind,
36    /// Characteristics in the service. Add with
37    /// [ServiceDefinition::add_characteristic]
38    characteristics: Vec<Characteristic>,
39    included_services: HashSet<ServiceId>,
40    /// Set of handles (characteristic or descriptor) used, to verify uniquness
41    /// of new handles added.
42    handles: HashSet<Handle>,
43}
44
45impl ServiceDefinition {
46    /// Make a new, empty service with a given id.
47    pub fn new(id: ServiceId, uuid: Uuid, kind: ServiceKind) -> Self {
48        Self {
49            id,
50            uuid,
51            kind,
52            characteristics: Default::default(),
53            included_services: Default::default(),
54            handles: HashSet::new(),
55        }
56    }
57
58    /// Add a characteristic to the definition.  If any duplicate handles or an
59    /// invalid configuration of descriptors are detected, an error is
60    /// returned.
61    pub fn add_characteristic(&mut self, characteristic: Characteristic) -> Result<()> {
62        let new_handles = characteristic.handles().collect();
63        if !self.handles.is_disjoint(&new_handles) {
64            return Err(Error::DuplicateHandle(
65                self.handles.intersection(&new_handles).copied().collect(),
66            ));
67        }
68        self.handles.extend(new_handles);
69        // TODO: check for more errors
70        self.characteristics.push(characteristic);
71        Ok(())
72    }
73
74    pub fn id(&self) -> ServiceId {
75        self.id
76    }
77
78    pub fn uuid(&self) -> Uuid {
79        self.uuid
80    }
81
82    pub fn kind(&self) -> ServiceKind {
83        self.kind
84    }
85
86    pub fn characteristics(&self) -> impl Iterator<Item = &Characteristic> {
87        self.characteristics.iter()
88    }
89
90    pub fn included(&self) -> impl Iterator<Item = ServiceId> + '_ {
91        self.included_services.iter().cloned()
92    }
93
94    /// Add a service to the definition.
95    pub fn add_service(&mut self, id: ServiceId) {
96        self.included_services.insert(id);
97    }
98}
99
100/// Services can be included in other services, and are included in the database
101/// when they are published.  All included services should be prepared before
102/// the service including them.  Publishing a service that includes other
103/// services will publish the included services, although the events associated
104/// with the included service will not be returned until the
105/// [LocalService::publish] is called.
106pub trait Server<T: crate::ServerTypes> {
107    /// Prepare to publish a service.
108    /// This service is not immediately visible in the local GATT server.
109    /// It will be published when the LocalService::publish is called.
110    /// If the returned LocalService is dropped, the service will be removed
111    /// from the Server.
112    fn prepare(&self, service: ServiceDefinition) -> T::LocalServiceFut;
113}
114
115pub trait LocalService<T: crate::ServerTypes> {
116    /// Publish the service.
117    /// Returns an EventStream providing Events to be processed by the local
118    /// service implementation.
119    /// Events will only be delivered to one ServiceEventStream at a time.
120    /// Calling publish while a previous ServiceEventStream is still active
121    /// will return a stream with only Err(AlreadyPublished).
122    fn publish(&self) -> T::ServiceEventStream;
123
124    /// Notify a characteristic.
125    /// Leave `peers` empty to notify all peers who have configured
126    /// notifications. Peers that have not configured for notifications will
127    /// not be notified.
128    fn notify(&self, characteristic: &Handle, data: &[u8], peers: &[PeerId]);
129
130    /// Indicate on a characteristic.
131    /// Leave `peers` empty to notify all peers who have configured
132    /// indications. Peers that have not configured for indications will
133    /// be skipped. Returns a stream which has items for each peer that
134    /// confirms the notification, and terminates when all peers have either
135    /// timed out or confirmed.
136    fn indicate(
137        &self,
138        characteristic: &Handle,
139        data: &[u8],
140        peers: &[PeerId],
141    ) -> T::IndicateConfirmationStream;
142}
143
144pub struct ConfirmationEvent {
145    peer_id: PeerId,
146    result: Result<()>,
147}
148
149impl ConfirmationEvent {
150    pub fn create_ack(peer_id: PeerId) -> Self {
151        Self { peer_id, result: Ok(()) }
152    }
153
154    pub fn create_error(peer_id: PeerId, error: Error) -> Self {
155        Self { peer_id, result: Err(error) }
156    }
157    pub fn peer_id(&self) -> PeerId {
158        self.peer_id
159    }
160
161    pub fn error(&self) -> Option<&Error> {
162        self.result.as_ref().err()
163    }
164
165    pub fn is_ok(&self) -> bool {
166        self.result.is_ok()
167    }
168
169    pub fn is_err(&self) -> bool {
170        self.result.is_err()
171    }
172}
173
174/// Responder that can send data that has been read from a characteristic.
175pub trait ReadResponder {
176    /// Respond with the data requested.  `value` may be shorter than requested.
177    fn respond(self, value: &[u8]);
178    /// Respond with an error.
179    fn error(self, error: GattError);
180}
181
182/// Responder that can acknowledge a write to a characteristic.
183pub trait WriteResponder {
184    /// Acknowledge the write. Will only send an acknowledgement if allowed by
185    /// the GATT protocol.
186    fn acknowledge(self);
187    /// Respond with an error.
188    fn error(self, error: GattError);
189}
190
191#[derive(Debug)]
192pub enum NotificationType {
193    Disable,
194    Notify,
195    Indicate,
196}
197
198#[non_exhaustive]
199pub enum ServiceEvent<T: crate::ServerTypes> {
200    /// Peer requests to read from a handle (characteritic or descriptor) at the
201    /// given offset.
202    Read { peer_id: PeerId, handle: Handle, offset: u32, responder: T::ReadResponder },
203    /// Peer has written a value to a handle (characteristic or descriptor) at
204    /// the given offset.
205    Write {
206        peer_id: PeerId,
207        handle: Handle,
208        offset: u32,
209        value: T::ServiceWriteType,
210        responder: T::WriteResponder,
211    },
212    /// Notification that a peer has configured a characteristic for indication
213    /// or notification.
214    ClientConfiguration { peer_id: PeerId, handle: Handle, notification_type: NotificationType },
215    /// Extra information about a peer is provided. This event may not be sent
216    /// by all implementations.
217    #[non_exhaustive]
218    PeerInfo { peer_id: PeerId, mtu: Option<u16>, connected: Option<bool> },
219}
220
221impl<T: crate::ServerTypes> ServiceEvent<T> {
222    pub fn peer_id(&self) -> PeerId {
223        match self {
224            Self::Read { peer_id, .. } => *peer_id,
225            Self::Write { peer_id, .. } => *peer_id,
226            Self::ClientConfiguration { peer_id, .. } => *peer_id,
227            Self::PeerInfo { peer_id, .. } => *peer_id,
228        }
229    }
230
231    pub fn peer_info(peer_id: PeerId, mtu: Option<u16>, connected: Option<bool>) -> Self {
232        Self::PeerInfo { peer_id, mtu, connected }
233    }
234}