• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Collection of Profile UUIDs and helpers to use them.
2 
3 use lazy_static::lazy_static;
4 use num_derive::{FromPrimitive, ToPrimitive};
5 use std::collections::{HashMap, HashSet};
6 use std::fmt::{Debug, Display, Formatter};
7 
8 use bt_topshim::btif::{Uuid, Uuid128Bit};
9 
10 // List of profile uuids
11 pub const A2DP_SINK: &str = "0000110B-0000-1000-8000-00805F9B34FB";
12 pub const A2DP_SOURCE: &str = "0000110A-0000-1000-8000-00805F9B34FB";
13 pub const ADV_AUDIO_DIST: &str = "0000110D-0000-1000-8000-00805F9B34FB";
14 pub const BAS: &str = "0000180F-0000-1000-8000-00805F9B34FB";
15 pub const DIS: &str = "0000180A-0000-1000-8000-00805F9B34FB";
16 pub const HSP: &str = "00001108-0000-1000-8000-00805F9B34FB";
17 pub const HSP_AG: &str = "00001112-0000-1000-8000-00805F9B34FB";
18 pub const HFP: &str = "0000111E-0000-1000-8000-00805F9B34FB";
19 pub const HFP_AG: &str = "0000111F-0000-1000-8000-00805F9B34FB";
20 pub const AVRCP_CONTROLLER: &str = "0000110E-0000-1000-8000-00805F9B34FB";
21 pub const AVRCP_TARGET: &str = "0000110C-0000-1000-8000-00805F9B34FB";
22 pub const OBEX_OBJECT_PUSH: &str = "00001105-0000-1000-8000-00805f9b34fb";
23 pub const HID: &str = "00001124-0000-1000-8000-00805f9b34fb";
24 pub const HOGP: &str = "00001812-0000-1000-8000-00805f9b34fb";
25 pub const PANU: &str = "00001115-0000-1000-8000-00805F9B34FB";
26 pub const NAP: &str = "00001116-0000-1000-8000-00805F9B34FB";
27 pub const BNEP: &str = "0000000f-0000-1000-8000-00805F9B34FB";
28 pub const PBAP_PCE: &str = "0000112e-0000-1000-8000-00805F9B34FB";
29 pub const PBAP_PSE: &str = "0000112f-0000-1000-8000-00805F9B34FB";
30 pub const MAP: &str = "00001134-0000-1000-8000-00805F9B34FB";
31 pub const MNS: &str = "00001133-0000-1000-8000-00805F9B34FB";
32 pub const MAS: &str = "00001132-0000-1000-8000-00805F9B34FB";
33 pub const SAP: &str = "0000112D-0000-1000-8000-00805F9B34FB";
34 pub const HEARING_AID: &str = "0000FDF0-0000-1000-8000-00805f9b34fb";
35 pub const LE_AUDIO: &str = "EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE";
36 pub const DIP: &str = "00001200-0000-1000-8000-00805F9B34FB";
37 pub const VOLUME_CONTROL: &str = "00001844-0000-1000-8000-00805F9B34FB";
38 pub const GENERIC_MEDIA_CONTROL: &str = "00001849-0000-1000-8000-00805F9B34FB";
39 pub const MEDIA_CONTROL: &str = "00001848-0000-1000-8000-00805F9B34FB";
40 pub const COORDINATED_SET: &str = "00001846-0000-1000-8000-00805F9B34FB";
41 pub const BASE_UUID: &str = "00000000-0000-1000-8000-00805F9B34FB";
42 
43 /// List of profiles that with known uuids.
44 #[derive(Clone, Debug, Hash, PartialEq, PartialOrd, Eq, Ord, FromPrimitive, ToPrimitive, Copy)]
45 #[repr(u32)]
46 pub enum Profile {
47     A2dpSink,
48     A2dpSource,
49     AdvAudioDist,
50     Bas,
51     Dis,
52     Hsp,
53     HspAg,
54     Hfp,
55     HfpAg,
56     AvrcpController,
57     AvrcpTarget,
58     ObexObjectPush,
59     Hid,
60     Hogp,
61     Panu,
62     Nap,
63     Bnep,
64     PbapPce,
65     PbapPse,
66     Map,
67     Mns,
68     Mas,
69     Sap,
70     HearingAid,
71     LeAudio,
72     Dip,
73     VolumeControl,
74     GenericMediaControl,
75     MediaControl,
76     CoordinatedSet,
77 }
78 
79 // Unsigned integer representation of UUIDs.
80 pub const BASE_UUID_NUM: u128 = 0x0000000000001000800000805f9b34fbu128;
81 pub const BASE_UUID_MASK: u128 = !(0xffffffffu128 << 96);
82 
83 impl Display for Profile {
fmt(&self, f: &mut Formatter) -> std::fmt::Result84     fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
85         Debug::fmt(self, f)
86     }
87 }
88 
89 /// Wraps a reference of Uuid128Bit, which is the raw array of bytes of UUID.
90 /// This is useful in implementing standard Rust traits which can't be implemented directly on
91 /// built-in types (Rust's Orphan Rule).
92 pub struct UuidWrapper<'a>(pub &'a Uuid128Bit);
93 
94 impl<'a> Display for UuidWrapper<'a> {
fmt(&self, f: &mut Formatter) -> std::fmt::Result95     fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
96         Uuid::format(&self.0, f)
97     }
98 }
99 
100 pub struct KnownUuidWrapper<'a>(pub &'a Uuid128Bit, pub &'a Profile);
101 
102 impl<'a> Display for KnownUuidWrapper<'a> {
fmt(&self, f: &mut Formatter) -> std::fmt::Result103     fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
104         let _ = Uuid::format(&self.0, f);
105         write!(f, ": {:?}", self.1)
106     }
107 }
108 
109 pub struct UuidHelper {}
110 
111 lazy_static! {
112     static ref SUPPORTED_PROFILES: HashSet<Profile> = [
113         Profile::A2dpSink,
114         Profile::A2dpSource,
115         Profile::AvrcpController,
116         Profile::AvrcpTarget,
117         Profile::Bas,
118         Profile::Hsp,
119         Profile::Hfp,
120         Profile::Hid,
121         Profile::Hogp,
122         Profile::Panu,
123         Profile::PbapPce,
124         Profile::Map,
125         Profile::HearingAid,
126         Profile::VolumeControl,
127         Profile::CoordinatedSet,
128     ]
129     .iter()
130     .cloned()
131     .collect();
132 }
133 
134 lazy_static! {
135     static ref PROFILES: HashMap<Uuid128Bit, Profile> = [
136         (UuidHelper::from_string(A2DP_SINK).unwrap(), Profile::A2dpSink),
137         (UuidHelper::from_string(A2DP_SOURCE).unwrap(), Profile::A2dpSource),
138         (UuidHelper::from_string(ADV_AUDIO_DIST).unwrap(), Profile::AdvAudioDist),
139         (UuidHelper::from_string(BAS).unwrap(), Profile::Bas),
140         (UuidHelper::from_string(DIS).unwrap(), Profile::Dis),
141         (UuidHelper::from_string(HSP).unwrap(), Profile::Hsp),
142         (UuidHelper::from_string(HSP_AG).unwrap(), Profile::HspAg),
143         (UuidHelper::from_string(HFP).unwrap(), Profile::Hfp),
144         (UuidHelper::from_string(HFP_AG).unwrap(), Profile::HfpAg),
145         (UuidHelper::from_string(AVRCP_CONTROLLER).unwrap(), Profile::AvrcpController),
146         (UuidHelper::from_string(AVRCP_TARGET).unwrap(), Profile::AvrcpTarget),
147         (UuidHelper::from_string(OBEX_OBJECT_PUSH).unwrap(), Profile::ObexObjectPush),
148         (UuidHelper::from_string(HID).unwrap(), Profile::Hid),
149         (UuidHelper::from_string(HOGP).unwrap(), Profile::Hogp),
150         (UuidHelper::from_string(PANU).unwrap(), Profile::Panu),
151         (UuidHelper::from_string(NAP).unwrap(), Profile::Nap),
152         (UuidHelper::from_string(BNEP).unwrap(), Profile::Bnep),
153         (UuidHelper::from_string(PBAP_PCE).unwrap(), Profile::PbapPce),
154         (UuidHelper::from_string(PBAP_PSE).unwrap(), Profile::PbapPse),
155         (UuidHelper::from_string(MAP).unwrap(), Profile::Map),
156         (UuidHelper::from_string(MNS).unwrap(), Profile::Mns),
157         (UuidHelper::from_string(MAS).unwrap(), Profile::Mas),
158         (UuidHelper::from_string(SAP).unwrap(), Profile::Sap),
159         (UuidHelper::from_string(HEARING_AID).unwrap(), Profile::HearingAid),
160         (UuidHelper::from_string(LE_AUDIO).unwrap(), Profile::LeAudio),
161         (UuidHelper::from_string(DIP).unwrap(), Profile::Dip),
162         (UuidHelper::from_string(VOLUME_CONTROL).unwrap(), Profile::VolumeControl),
163         (UuidHelper::from_string(GENERIC_MEDIA_CONTROL).unwrap(), Profile::GenericMediaControl),
164         (UuidHelper::from_string(MEDIA_CONTROL).unwrap(), Profile::MediaControl),
165         (UuidHelper::from_string(COORDINATED_SET).unwrap(), Profile::CoordinatedSet),
166     ]
167     .iter()
168     .cloned()
169     .collect();
170 }
171 
172 lazy_static! {
173     static ref PROFILES_UUIDS: HashMap<Profile, Uuid128Bit> =
174         PROFILES.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
175 }
176 
177 impl UuidHelper {
178     /// Checks whether a UUID corresponds to a currently enabled profile.
is_profile_supported(profile: &Profile) -> bool179     pub fn is_profile_supported(profile: &Profile) -> bool {
180         SUPPORTED_PROFILES.contains(profile)
181     }
182 
183     /// Converts a UUID to a known profile enum.
is_known_profile(uuid: &Uuid128Bit) -> Option<Profile>184     pub fn is_known_profile(uuid: &Uuid128Bit) -> Option<Profile> {
185         PROFILES.get(uuid).cloned()
186     }
187 
get_supported_profiles() -> HashSet<Profile>188     pub fn get_supported_profiles() -> HashSet<Profile> {
189         SUPPORTED_PROFILES.clone()
190     }
191 
192     /// Converts a profile enum to its UUID if known.
get_profile_uuid(profile: &Profile) -> Option<&Uuid128Bit>193     pub fn get_profile_uuid(profile: &Profile) -> Option<&Uuid128Bit> {
194         PROFILES_UUIDS.get(profile)
195     }
196 
197     /// Converts a UUID byte array into a formatted string.
to_string(uuid: &Uuid128Bit) -> String198     pub fn to_string(uuid: &Uuid128Bit) -> String {
199         UuidWrapper(&uuid).to_string()
200     }
201 
202     /// If a uuid is known to be a certain service, convert it into a formatted
203     /// string that shows the service name. Else just format the uuid.
known_uuid_to_string(uuid: &Uuid128Bit) -> String204     pub fn known_uuid_to_string(uuid: &Uuid128Bit) -> String {
205         if let Some(p) = Self::is_known_profile(uuid) {
206             return KnownUuidWrapper(&uuid, &p).to_string();
207         }
208 
209         UuidHelper::to_string(uuid)
210     }
211 
212     /// Converts a well-formatted UUID string to a UUID byte array.
213     /// The UUID string should be in the format:
214     /// 12345678-1234-1234-1234-1234567890
from_string<S: Into<String>>(raw: S) -> Option<Uuid128Bit>215     pub fn from_string<S: Into<String>>(raw: S) -> Option<Uuid128Bit> {
216         let raw: String = raw.into();
217 
218         // Make sure input is valid length and formatting
219         let s = raw.split('-').collect::<Vec<&str>>();
220         if s.len() != 5 || raw.len() != 36 {
221             return None;
222         }
223 
224         let mut uuid: Uuid128Bit = [0; 16];
225         let mut idx = 0;
226         for section in s.iter() {
227             for i in (0..section.len()).step_by(2) {
228                 uuid[idx] = match u8::from_str_radix(&section[i..i + 2], 16) {
229                     Ok(res) => res,
230                     Err(_) => {
231                         return None;
232                     }
233                 };
234                 idx = idx + 1;
235             }
236         }
237 
238         Some(uuid)
239     }
240 
241     /// Parses an 128-bit UUID into a byte array of shortest representation.
get_shortest_slice(uuid: &Uuid128Bit) -> &[u8]242     pub fn get_shortest_slice(uuid: &Uuid128Bit) -> &[u8] {
243         if UuidHelper::in_16bit_uuid_range(uuid) {
244             return &uuid[2..4];
245         } else if UuidHelper::in_32bit_uuid_range(uuid) {
246             return &uuid[0..4];
247         } else {
248             return &uuid[..];
249         }
250     }
251 
252     /// Checks whether the UUID value is in the 16-bit Bluetooth UUID range.
in_16bit_uuid_range(uuid: &Uuid128Bit) -> bool253     fn in_16bit_uuid_range(uuid: &Uuid128Bit) -> bool {
254         if !UuidHelper::in_32bit_uuid_range(uuid) {
255             return false;
256         }
257         uuid[0] == 0 && uuid[1] == 0
258     }
259 
260     /// Checks whether the UUID value is in the 32-bit Bluetooth UUID range.
in_32bit_uuid_range(uuid: &Uuid128Bit) -> bool261     fn in_32bit_uuid_range(uuid: &Uuid128Bit) -> bool {
262         let num = u128::from_be_bytes(*uuid);
263         (num & BASE_UUID_MASK) == BASE_UUID_NUM
264     }
265 
266     // Temporary util that covers only basic string conversion.
267     // TODO(b/193685325): Implement more UUID utils by using Uuid from gd/hci/uuid.h with cxx.
parse_string<T: Into<String>>(uuid: T) -> Option<Uuid>268     pub fn parse_string<T: Into<String>>(uuid: T) -> Option<Uuid> {
269         let uuid = uuid.into();
270 
271         // Strip un-needed characters before parsing to handle the common
272         // case of including dashes in UUID strings. UUID expects only
273         // 0-9, a-f, A-F with no other characters. |is_digit| with radix
274         // 16 (hex) supports that exact behavior.
275         let uuid = uuid.chars().filter(|char| char.is_digit(16)).collect::<String>();
276         if uuid.len() != 32 {
277             return None;
278         }
279 
280         let mut raw = [0; 16];
281 
282         for i in 0..16 {
283             raw[i] = u8::from_str_radix(&uuid[i * 2..i * 2 + 2], 16).ok()?;
284         }
285 
286         Some(Uuid::from(raw))
287     }
288 }
289 
290 #[cfg(test)]
291 mod tests {
292     use super::*;
293 
294     #[test]
test_uuidhelper()295     fn test_uuidhelper() {
296         for (uuid, _) in PROFILES.iter() {
297             let converted = UuidHelper::from_string(UuidHelper::to_string(&uuid));
298             assert_eq!(converted.is_some(), true);
299             converted.and_then::<Uuid128Bit, _>(|uu: Uuid128Bit| {
300                 assert_eq!(&uu, uuid);
301                 None
302             });
303         }
304     }
305 
306     #[test]
test_get_shortest_slice()307     fn test_get_shortest_slice() {
308         let uuid_16 = UuidHelper::from_string("0000fef3-0000-1000-8000-00805f9b34fb").unwrap();
309         assert_eq!(UuidHelper::get_shortest_slice(&uuid_16), [0xfe, 0xf3]);
310 
311         let uuid_32 = UuidHelper::from_string("00112233-0000-1000-8000-00805f9b34fb").unwrap();
312         assert_eq!(UuidHelper::get_shortest_slice(&uuid_32), [0x00, 0x11, 0x22, 0x33]);
313 
314         let uuid_128 = UuidHelper::from_string("00112233-4455-6677-8899-aabbccddeeff").unwrap();
315         assert_eq!(
316             UuidHelper::get_shortest_slice(&uuid_128),
317             [
318                 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
319                 0xee, 0xff
320             ]
321         );
322     }
323 }
324