1use bt_gatt::server::{LocalService, Server as _, ServiceDefinition, ServiceId};
8use bt_gatt::types::{
9 AttributePermissions, CharacteristicProperty, Handle, SecurityLevels, ServiceKind,
10};
11use bt_gatt::Characteristic;
12use futures::Future;
13use pin_project::pin_project;
14use std::collections::HashMap;
15
16use crate::types::{
17 BroadcastReceiveState, SourceId, BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID,
18 BROADCAST_AUDIO_SCAN_SERVICE_UUID, BROADCAST_RECEIVE_STATE_UUID,
19};
20
21pub mod error;
22pub use error::Error;
23
24const BASS_SERVICE_ID: ServiceId = ServiceId::new(1);
26
27const CONTROL_POINT_HANDLE: Handle = Handle(1);
29
30const MAX_RECEIVE_STATES: usize = 255;
33
34#[derive(Debug)]
37pub(crate) struct PublishedReceiveStateCharacteristic {
38 source_id: SourceId,
41 handle: Handle,
43 state: BroadcastReceiveState,
45}
46
47#[pin_project(project = LocalServiceProj)]
49enum LocalServiceState<T: bt_gatt::ServerTypes> {
50 NotPublished,
52 Preparing {
54 #[pin]
55 fut: T::LocalServiceFut,
56 },
57 Published { service: T::LocalService },
59}
60
61impl<T: bt_gatt::ServerTypes> Default for LocalServiceState<T> {
62 fn default() -> Self {
63 Self::NotPublished
64 }
65}
66
67impl<T: bt_gatt::ServerTypes> LocalServiceState<T> {
68 fn is_published(&self) -> bool {
69 matches!(self, LocalServiceState::Published { .. })
70 }
71
72 fn poll_publish(
73 mut self: std::pin::Pin<&mut Self>,
74 cx: &mut std::task::Context<'_>,
75 ) -> std::task::Poll<Result<(), Error>> {
76 match self.as_mut().project() {
77 LocalServiceProj::NotPublished => std::task::Poll::Pending,
78 LocalServiceProj::Preparing { fut } => {
79 let service_result = futures::ready!(fut.poll(cx));
80 match service_result {
81 Ok(service) => {
82 let _ = service.publish();
83 self.set(LocalServiceState::Published { service });
84 std::task::Poll::Ready(Ok(()))
85 }
86 Err(e) => {
87 self.set(LocalServiceState::NotPublished);
88 std::task::Poll::Ready(Err(Error::Gatt(e)))
89 }
90 }
91 }
92 LocalServiceProj::Published { .. } => std::task::Poll::Ready(Ok(())),
93 }
94 }
95}
96
97#[derive(Default)]
99pub struct ServerBuilder {
100 receive_states: Vec<BroadcastReceiveState>,
103}
104
105impl ServerBuilder {
106 pub fn new() -> Self {
107 Self::default()
108 }
109
110 pub fn add_receive_state_characteristic(mut self, state: BroadcastReceiveState) -> Self {
112 self.receive_states.push(state);
113 self
114 }
115
116 fn build_control_point() -> Characteristic {
119 let cp_properties =
120 CharacteristicProperty::Write | CharacteristicProperty::WriteWithoutResponse;
121 Characteristic {
122 handle: CONTROL_POINT_HANDLE,
123 uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID,
124 properties: cp_properties.clone(),
125 permissions: AttributePermissions::with_levels(
126 &cp_properties,
127 &SecurityLevels::encryption_required(),
128 ),
129 descriptors: Vec::new(),
130 }
131 }
132
133 fn build_receive_state(handle: Handle) -> Characteristic {
136 let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify;
137 Characteristic {
138 handle,
139 uuid: BROADCAST_RECEIVE_STATE_UUID,
140 properties: properties.clone(),
141 permissions: AttributePermissions::with_levels(
142 &properties,
143 &SecurityLevels::encryption_required(),
144 ),
145 descriptors: Vec::new(),
146 }
147 }
148
149 pub fn build<T: bt_gatt::ServerTypes>(self) -> Result<Server<T>, Error> {
152 if self.receive_states.is_empty() {
155 return Err(Error::MissingReceiveState);
156 }
157 if self.receive_states.len() > MAX_RECEIVE_STATES {
158 return Err(Error::ExceedsMaxReceiveStates);
159 }
160
161 let mut service_def = ServiceDefinition::new(
162 BASS_SERVICE_ID,
163 BROADCAST_AUDIO_SCAN_SERVICE_UUID,
164 ServiceKind::Primary,
165 );
166
167 let _ = service_def.add_characteristic(Self::build_control_point());
168
169 const FIRST_RECEIVE_STATE_HANDLE: Handle = Handle(2);
171 let num_receive_states = self.receive_states.len();
172 let mut receive_state_characteristics = HashMap::with_capacity(num_receive_states);
173 let mut source_id_to_handle = HashMap::with_capacity(num_receive_states);
174 for (i, mut state) in self.receive_states.into_iter().enumerate() {
175 let source_id = (i + 1) as u8;
176 let handle = Handle(FIRST_RECEIVE_STATE_HANDLE.0 + i as u64);
177 let _ = service_def.add_characteristic(Self::build_receive_state(handle));
178
179 if let BroadcastReceiveState::NonEmpty(ref mut receive_state) = state {
182 receive_state.source_id = source_id;
183 }
184
185 receive_state_characteristics
186 .insert(handle, PublishedReceiveStateCharacteristic { source_id, handle, state });
187 source_id_to_handle.insert(source_id, handle);
188 }
189
190 let next_source_id = (num_receive_states + 1) as SourceId;
191
192 Ok(Server {
193 service_def,
194 local_service: Default::default(),
195 control_point_handle: CONTROL_POINT_HANDLE,
196 receive_state_characteristics,
197 source_id_to_handle,
198 next_source_id,
199 })
200 }
201}
202
203#[pin_project]
208pub struct Server<T: bt_gatt::ServerTypes> {
209 service_def: ServiceDefinition,
210 #[pin]
211 local_service: LocalServiceState<T>,
212 control_point_handle: Handle,
213 receive_state_characteristics: HashMap<Handle, PublishedReceiveStateCharacteristic>,
215 source_id_to_handle: HashMap<SourceId, Handle>,
217 next_source_id: SourceId,
220}
221
222impl<T: bt_gatt::ServerTypes> Server<T> {
223 pub fn is_published(&self) -> bool {
225 self.local_service.is_published()
226 }
227
228 #[cfg(test)]
232 pub(crate) fn poll_publish(
233 self: std::pin::Pin<&mut Self>,
234 cx: &mut std::task::Context<'_>,
235 ) -> std::task::Poll<Result<(), Error>> {
236 self.project().local_service.poll_publish(cx)
237 }
238
239 pub fn publish(&mut self, server: T::Server) -> Result<(), Error> {
241 if matches!(
242 self.local_service,
243 LocalServiceState::Preparing { .. } | LocalServiceState::Published { .. }
244 ) {
245 return Err(Error::AlreadyPublished);
246 }
247
248 let LocalServiceState::NotPublished = std::mem::replace(
249 &mut self.local_service,
250 LocalServiceState::Preparing { fut: server.prepare(self.service_def.clone()) },
251 ) else {
252 unreachable!();
253 };
254 Ok(())
255 }
256
257 #[cfg(test)]
260 fn handle_read(
261 &self,
262 handle: Handle,
263 offset: usize,
264 responder: impl bt_gatt::server::ReadResponder,
265 ) {
266 use bt_common::packet_encoding::Encodable;
267 use bt_gatt::types::GattError;
268
269 if handle == self.control_point_handle {
270 responder.error(GattError::ReadNotPermitted);
271 return;
272 }
273
274 let Some(slot) = self.receive_state_characteristics.get(&handle) else {
275 responder.error(GattError::InvalidHandle);
276 return;
277 };
278
279 let mut buf = vec![0u8; slot.state.encoded_len()];
280 if slot.state.encode(&mut buf).is_ok() {
281 if offset > buf.len() {
282 responder.error(GattError::InvalidOffset);
283 } else {
284 responder.respond(&buf[offset..]);
285 }
286 } else {
287 responder.error(GattError::UnlikelyError);
288 }
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use bt_bap::types::BroadcastId;
296 use bt_common::core::{AddressType, AdvertisingSetId};
297 use bt_gatt::server::ReadResponder;
298 use bt_gatt::test_utils::{FakeServer, FakeServerEvent, FakeTypes};
299 use bt_gatt::types::GattError;
300 use futures::FutureExt;
301 use parking_lot::Mutex;
302 use std::sync::Arc;
303
304 use crate::types::{EncryptionStatus, PaSyncState, ReceiveState};
305
306 struct TestReadResponder {
307 result: Arc<Mutex<Option<Result<Vec<u8>, GattError>>>>,
308 }
309
310 impl ReadResponder for TestReadResponder {
311 fn respond(self, value: &[u8]) {
312 *self.result.lock() = Some(Ok(value.to_vec()));
313 }
314
315 fn error(self, error: GattError) {
316 *self.result.lock() = Some(Err(error));
317 }
318 }
319
320 fn make_test_receive_state(source_id: SourceId) -> BroadcastReceiveState {
321 BroadcastReceiveState::NonEmpty(ReceiveState::new(
322 source_id,
323 AddressType::Public,
324 [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
325 AdvertisingSetId::try_from(1).unwrap(),
326 BroadcastId::try_from(0x123456).unwrap(),
327 PaSyncState::NotSynced,
328 EncryptionStatus::NotEncrypted,
329 vec![],
330 ))
331 }
332
333 #[test]
334 fn empty_builder_fails() {
335 let builder = ServerBuilder::new();
336 assert!(matches!(builder.build::<FakeTypes>(), Err(Error::MissingReceiveState)));
337 }
338
339 #[test]
340 fn too_many_receive_states_fails() {
341 let mut builder = ServerBuilder::new();
342 for _ in 0..=MAX_RECEIVE_STATES {
343 builder = builder.add_receive_state_characteristic(BroadcastReceiveState::Empty);
344 }
345 assert!(matches!(builder.build::<FakeTypes>(), Err(Error::ExceedsMaxReceiveStates)));
346 }
347
348 #[test]
349 fn publish_server() {
350 use futures::task::Context;
351 use futures::StreamExt;
352 use std::task::Poll;
353
354 let mut noop_cx = Context::from_waker(futures::task::noop_waker_ref());
355
356 let mut server = ServerBuilder::new()
357 .add_receive_state_characteristic(BroadcastReceiveState::Empty)
358 .add_receive_state_characteristic(make_test_receive_state(2))
359 .build::<FakeTypes>()
360 .expect("building server works");
361
362 assert!(!server.is_published());
363
364 let (fake_gatt_server, mut event_receiver) = FakeServer::new();
365 let mut event_stream = event_receiver.next();
366
367 assert!(server.publish(fake_gatt_server.clone()).is_ok());
368 assert!(server.publish(fake_gatt_server).is_err());
369
370 assert!(std::pin::Pin::new(&mut server).poll_publish(&mut noop_cx).is_ready());
372 assert!(server.is_published());
373
374 let Poll::Ready(Some(FakeServerEvent::Published { id, definition })) =
375 event_stream.poll_unpin(&mut noop_cx)
376 else {
377 panic!("Expected published event");
378 };
379
380 assert_eq!(id, BASS_SERVICE_ID);
381 assert_eq!(definition.uuid(), BROADCAST_AUDIO_SCAN_SERVICE_UUID);
382 assert_eq!(definition.characteristics().count(), 3);
383 }
384
385 #[test]
386 fn read_receive_state() {
387 let server = ServerBuilder::new()
388 .add_receive_state_characteristic(BroadcastReceiveState::Empty)
389 .add_receive_state_characteristic(make_test_receive_state(2))
390 .build::<FakeTypes>()
391 .expect("building server works");
392
393 let res = Arc::new(Mutex::new(None));
395 server.handle_read(Handle(2), 0, TestReadResponder { result: res.clone() });
396 assert_eq!(res.lock().take().unwrap().expect("ok"), vec![]);
397
398 let res = Arc::new(Mutex::new(None));
400 server.handle_read(Handle(3), 0, TestReadResponder { result: res.clone() });
401 assert!(!res.lock().take().unwrap().expect("ok").is_empty());
402
403 let res = Arc::new(Mutex::new(None));
405 server.handle_read(Handle(99), 0, TestReadResponder { result: res.clone() });
406 assert_eq!(res.lock().take().unwrap().expect_err("err"), GattError::InvalidHandle);
407 }
408
409 #[test]
410 fn read_control_point_fails() {
411 let server = ServerBuilder::new()
412 .add_receive_state_characteristic(BroadcastReceiveState::Empty)
413 .build::<FakeTypes>()
414 .expect("building server works");
415
416 let res = Arc::new(Mutex::new(None));
417 server.handle_read(CONTROL_POINT_HANDLE, 0, TestReadResponder { result: res.clone() });
418 assert_eq!(res.lock().take().unwrap().expect_err("err"), GattError::ReadNotPermitted);
419 }
420
421 #[test]
422 fn read_receive_state_invalid_offset() {
423 let server = ServerBuilder::new()
424 .add_receive_state_characteristic(BroadcastReceiveState::Empty)
425 .build::<FakeTypes>()
426 .expect("building server works");
427
428 let res = Arc::new(Mutex::new(None));
429 server.handle_read(Handle(2), 10, TestReadResponder { result: res.clone() });
430 assert_eq!(res.lock().take().unwrap().expect_err("err"), GattError::InvalidOffset);
431 }
432
433 #[test]
434 fn poll_publish_failure() {
435 use futures::task::Context;
436 use std::task::Poll;
437
438 let mut noop_cx = Context::from_waker(futures::task::noop_waker_ref());
439
440 let mut server = ServerBuilder::new()
441 .add_receive_state_characteristic(BroadcastReceiveState::Empty)
442 .build::<FakeTypes>()
443 .expect("building server works");
444
445 let (fake_gatt_server, _event_receiver) = FakeServer::new();
446 fake_gatt_server
447 .set_next_prepare_result(Err(bt_gatt::types::Error::Gatt(GattError::UnlikelyError)));
448
449 assert!(server.publish(fake_gatt_server.clone()).is_ok());
450
451 assert!(matches!(
452 std::pin::Pin::new(&mut server).poll_publish(&mut noop_cx),
453 Poll::Ready(Err(Error::Gatt(bt_gatt::types::Error::Gatt(GattError::UnlikelyError))))
454 ));
455
456 assert!(!server.is_published());
458 }
459
460 #[test]
461 fn builder_normalizes_source_id() {
462 let state = make_test_receive_state(99);
464 let server = ServerBuilder::new()
465 .add_receive_state_characteristic(state)
466 .build::<FakeTypes>()
467 .expect("building server works");
468
469 let res = Arc::new(Mutex::new(None));
471 server.handle_read(Handle(2), 0, TestReadResponder { result: res.clone() });
472 let buf = res.lock().take().unwrap().expect("ok");
473 assert_eq!(buf[0], 1);
474 }
475}