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}