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(§ion[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