bt_pacs/server/
types.rs

1// Copyright 2024 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
5//! Define types and trait implementations specific to the PACS server.
6
7use bt_gatt::Characteristic;
8use bt_gatt::client::FromCharacteristic;
9use bt_gatt::types::{
10    AttributePermissions, CharacteristicProperties, CharacteristicProperty, Handle, SecurityLevels,
11};
12
13use std::collections::HashSet;
14
15use crate::*;
16
17pub(crate) const SUPPORTED_AUDIO_CONTEXTS_HANDLE: Handle = Handle(1);
18pub(crate) const AVAILABLE_AUDIO_CONTEXTS_HANDLE: Handle = Handle(2);
19pub(crate) const HANDLE_OFFSET: u64 = 3;
20
21impl From<&SupportedAudioContexts> for Characteristic {
22    fn from(_value: &SupportedAudioContexts) -> Self {
23        // TODO(b/309015071): implement optional properties.
24        let properties: CharacteristicProperties = CharacteristicProperty::Read.into();
25
26        Characteristic {
27            handle: SUPPORTED_AUDIO_CONTEXTS_HANDLE,
28            uuid: <SupportedAudioContexts as FromCharacteristic>::UUID,
29            properties: properties.clone(),
30            permissions: AttributePermissions::with_levels(
31                &properties,
32                &SecurityLevels::encryption_required(),
33            ),
34            descriptors: Vec::new(),
35        }
36    }
37}
38
39impl From<&AvailableAudioContexts> for Characteristic {
40    fn from(_value: &AvailableAudioContexts) -> Self {
41        let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify;
42
43        Characteristic {
44            handle: AVAILABLE_AUDIO_CONTEXTS_HANDLE,
45            uuid: <AvailableAudioContexts as FromCharacteristic>::UUID,
46            properties: properties.clone(),
47            permissions: AttributePermissions::with_levels(
48                &properties,
49                &SecurityLevels::encryption_required(),
50            ),
51            descriptors: Vec::new(),
52        }
53    }
54}
55
56impl From<&SourcePac> for Characteristic {
57    fn from(value: &SourcePac) -> Self {
58        // TODO(b/309015071): implement optional properties.
59        let properties: CharacteristicProperties = CharacteristicProperty::Read.into();
60
61        Characteristic {
62            handle: value.handle,
63            uuid: <SourcePac as FromCharacteristic>::UUID,
64            properties: properties.clone(),
65            permissions: AttributePermissions::with_levels(
66                &properties,
67                &SecurityLevels::encryption_required(),
68            ),
69            descriptors: Vec::new(),
70        }
71    }
72}
73
74impl From<&SinkPac> for Characteristic {
75    fn from(value: &SinkPac) -> Self {
76        // TODO(b/309015071): implement optional properties.
77        let properties: CharacteristicProperties = CharacteristicProperty::Read.into();
78
79        Characteristic {
80            handle: value.handle,
81            uuid: <SinkPac as FromCharacteristic>::UUID,
82            properties: properties.clone(),
83            permissions: AttributePermissions::with_levels(
84                &properties,
85                &SecurityLevels::encryption_required(),
86            ),
87            descriptors: Vec::new(),
88        }
89    }
90}
91
92impl From<&SourceAudioLocations> for Characteristic {
93    fn from(value: &SourceAudioLocations) -> Self {
94        // TODO(b/309015071): implement optional properties.
95        let properties: CharacteristicProperties = CharacteristicProperty::Read.into();
96
97        Characteristic {
98            handle: value.handle,
99            uuid: <SourceAudioLocations as FromCharacteristic>::UUID,
100            properties: properties.clone(),
101            permissions: AttributePermissions::with_levels(
102                &properties,
103                &SecurityLevels::encryption_required(),
104            ),
105            descriptors: Vec::new(),
106        }
107    }
108}
109
110impl From<&SinkAudioLocations> for Characteristic {
111    fn from(value: &SinkAudioLocations) -> Self {
112        // TODO(b/309015071): implement optional properties.
113        let properties: CharacteristicProperties = CharacteristicProperty::Read.into();
114
115        Characteristic {
116            handle: value.handle,
117            uuid: <SinkAudioLocations as FromCharacteristic>::UUID,
118            properties: properties.clone(),
119            permissions: AttributePermissions::with_levels(
120                &properties,
121                &SecurityLevels::encryption_required(),
122            ),
123            descriptors: Vec::new(),
124        }
125    }
126}
127
128#[derive(Default)]
129pub struct AudioContexts {
130    pub(crate) sink: HashSet<ContextType>,
131    pub(crate) source: HashSet<ContextType>,
132}
133
134impl AudioContexts {
135    pub fn new(sink: HashSet<ContextType>, source: HashSet<ContextType>) -> Self {
136        AudioContexts { sink, source }
137    }
138}
139
140/// A single PAC characteristic consists of 1 or more PAC records.
141pub type PacRecords = Vec<PacRecord>;
142
143#[derive(Debug, PartialEq)]
144pub(crate) enum PublishedAudioCapability {
145    Sink(SinkPac),
146    Source(SourcePac),
147}
148
149impl PublishedAudioCapability {
150    pub fn new_sink(handle: Handle, records: PacRecords) -> Self {
151        Self::Sink(SinkPac { handle: handle, capabilities: records })
152    }
153
154    pub fn new_source(handle: Handle, records: PacRecords) -> Self {
155        Self::Source(SourcePac { handle: handle, capabilities: records })
156    }
157
158    #[cfg(test)]
159    pub fn is_sink(&self) -> bool {
160        match self {
161            PublishedAudioCapability::Sink(_) => true,
162            PublishedAudioCapability::Source(_) => false,
163        }
164    }
165
166    #[cfg(test)]
167    pub fn is_source(&self) -> bool {
168        match self {
169            PublishedAudioCapability::Sink(_) => false,
170            PublishedAudioCapability::Source(_) => true,
171        }
172    }
173
174    #[cfg(test)]
175    pub fn pac_records(&self) -> &Vec<PacRecord> {
176        match self {
177            PublishedAudioCapability::Sink(pac) => &pac.capabilities,
178            PublishedAudioCapability::Source(pac) => &pac.capabilities,
179        }
180    }
181
182    /// Encode into PAC characteristic format as defined in PACS v1.0.1
183    /// Table 3.2/3.4.
184    pub(crate) fn encode(&self) -> Vec<u8> {
185        match self {
186            PublishedAudioCapability::Sink(pac) => pac_records_into_char_value(&pac.capabilities),
187            PublishedAudioCapability::Source(pac) => pac_records_into_char_value(&pac.capabilities),
188        }
189    }
190}
191
192impl From<&PublishedAudioCapability> for Characteristic {
193    fn from(value: &PublishedAudioCapability) -> Self {
194        match value {
195            PublishedAudioCapability::Sink(pac) => pac.into(),
196            PublishedAudioCapability::Source(pac) => pac.into(),
197        }
198    }
199}