1 //! Collection of Profile UUIDs and helpers to use them. 2 3 use num_derive::{FromPrimitive, ToPrimitive}; 4 use std::collections::{HashMap, HashSet}; 5 use std::fmt::{Debug, Display, Formatter}; 6 use std::sync::LazyLock; 7 8 use bt_topshim::btif::Uuid; 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 = "0000184E-0000-1000-8000-00805F9B34FB"; 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 descriptor uuids 44 pub const CCCD_UUID: &str = "00002902-0000-1000-8000-00805f9b34fb"; 45 46 /// List of profiles that with known uuids. 47 /// Append new profiles to the end of the enum. Do not insert it in the middle. 48 #[derive(Clone, Debug, Hash, PartialEq, PartialOrd, Eq, Ord, FromPrimitive, ToPrimitive, Copy)] 49 #[repr(u32)] 50 pub enum Profile { 51 A2dpSink, 52 A2dpSource, 53 AdvAudioDist, 54 Bas, 55 Dis, 56 Hsp, 57 HspAg, 58 Hfp, 59 HfpAg, 60 AvrcpController, 61 AvrcpTarget, 62 ObexObjectPush, 63 Hid, 64 Hogp, 65 Panu, 66 Nap, 67 Bnep, 68 PbapPce, 69 PbapPse, 70 Map, 71 Mns, 72 Mas, 73 Sap, 74 HearingAid, 75 LeAudio, 76 Dip, 77 VolumeControl, 78 GenericMediaControl, 79 MediaControl, 80 CoordinatedSet, 81 } 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 pub struct UuidHelper {} 90 91 // AVRCP fights with A2DP when initializing, so let's initiate profiles in a known good order. 92 // Specifically, A2DP must be initialized before AVRCP. 93 // TODO (b/286991526): remove after issue is resolved 94 static ORDERED_SUPPORTED_PROFILES: LazyLock<Vec<Profile>> = LazyLock::new(|| { 95 vec![ 96 Profile::A2dpSink, 97 Profile::A2dpSource, 98 Profile::AvrcpController, 99 Profile::AvrcpTarget, 100 Profile::Bas, 101 Profile::Hsp, 102 Profile::Hfp, 103 Profile::Hid, 104 Profile::Hogp, 105 Profile::LeAudio, 106 Profile::Panu, 107 Profile::PbapPce, 108 Profile::Map, 109 Profile::HearingAid, 110 Profile::VolumeControl, 111 Profile::CoordinatedSet, 112 ] 113 }); 114 115 static SUPPORTED_PROFILES: LazyLock<HashSet<Profile>> = 116 LazyLock::new(|| ORDERED_SUPPORTED_PROFILES.iter().cloned().collect()); 117 118 static PROFILES: LazyLock<HashMap<Uuid, Profile>> = LazyLock::new(|| { 119 [ 120 (Uuid::from_string(A2DP_SINK).unwrap(), Profile::A2dpSink), 121 (Uuid::from_string(A2DP_SOURCE).unwrap(), Profile::A2dpSource), 122 (Uuid::from_string(ADV_AUDIO_DIST).unwrap(), Profile::AdvAudioDist), 123 (Uuid::from_string(BAS).unwrap(), Profile::Bas), 124 (Uuid::from_string(DIS).unwrap(), Profile::Dis), 125 (Uuid::from_string(HSP).unwrap(), Profile::Hsp), 126 (Uuid::from_string(HSP_AG).unwrap(), Profile::HspAg), 127 (Uuid::from_string(HFP).unwrap(), Profile::Hfp), 128 (Uuid::from_string(HFP_AG).unwrap(), Profile::HfpAg), 129 (Uuid::from_string(AVRCP_CONTROLLER).unwrap(), Profile::AvrcpController), 130 (Uuid::from_string(AVRCP_TARGET).unwrap(), Profile::AvrcpTarget), 131 (Uuid::from_string(OBEX_OBJECT_PUSH).unwrap(), Profile::ObexObjectPush), 132 (Uuid::from_string(HID).unwrap(), Profile::Hid), 133 (Uuid::from_string(HOGP).unwrap(), Profile::Hogp), 134 (Uuid::from_string(PANU).unwrap(), Profile::Panu), 135 (Uuid::from_string(NAP).unwrap(), Profile::Nap), 136 (Uuid::from_string(BNEP).unwrap(), Profile::Bnep), 137 (Uuid::from_string(PBAP_PCE).unwrap(), Profile::PbapPce), 138 (Uuid::from_string(PBAP_PSE).unwrap(), Profile::PbapPse), 139 (Uuid::from_string(MAP).unwrap(), Profile::Map), 140 (Uuid::from_string(MNS).unwrap(), Profile::Mns), 141 (Uuid::from_string(MAS).unwrap(), Profile::Mas), 142 (Uuid::from_string(SAP).unwrap(), Profile::Sap), 143 (Uuid::from_string(HEARING_AID).unwrap(), Profile::HearingAid), 144 (Uuid::from_string(LE_AUDIO).unwrap(), Profile::LeAudio), 145 (Uuid::from_string(DIP).unwrap(), Profile::Dip), 146 (Uuid::from_string(VOLUME_CONTROL).unwrap(), Profile::VolumeControl), 147 (Uuid::from_string(GENERIC_MEDIA_CONTROL).unwrap(), Profile::GenericMediaControl), 148 (Uuid::from_string(MEDIA_CONTROL).unwrap(), Profile::MediaControl), 149 (Uuid::from_string(COORDINATED_SET).unwrap(), Profile::CoordinatedSet), 150 ] 151 .iter() 152 .cloned() 153 .collect() 154 }); 155 156 static PROFILES_UUIDS: LazyLock<HashMap<Profile, Uuid>> = 157 LazyLock::new(|| PROFILES.iter().map(|(k, v)| (*v, *k)).collect()); 158 159 impl UuidHelper { 160 /// Checks whether a UUID corresponds to a currently enabled profile. is_profile_supported(profile: &Profile) -> bool161 pub fn is_profile_supported(profile: &Profile) -> bool { 162 SUPPORTED_PROFILES.contains(profile) 163 } 164 165 /// Converts a UUID to a known profile enum. is_known_profile(uuid: &Uuid) -> Option<Profile>166 pub fn is_known_profile(uuid: &Uuid) -> Option<Profile> { 167 PROFILES.get(uuid).cloned() 168 } 169 170 // AVRCP fights with A2DP when initializing, so let's initiate profiles in a known good order. 171 // TODO (b/286991526): remove after issue is resolved get_ordered_supported_profiles() -> Vec<Profile>172 pub fn get_ordered_supported_profiles() -> Vec<Profile> { 173 ORDERED_SUPPORTED_PROFILES.clone() 174 } 175 get_supported_profiles() -> HashSet<Profile>176 pub fn get_supported_profiles() -> HashSet<Profile> { 177 SUPPORTED_PROFILES.clone() 178 } 179 180 /// Converts a profile enum to its UUID if known. get_profile_uuid(profile: &Profile) -> Option<&Uuid>181 pub fn get_profile_uuid(profile: &Profile) -> Option<&Uuid> { 182 PROFILES_UUIDS.get(profile) 183 } 184 185 /// If a uuid is known to be a certain service, convert it into a formatted 186 /// string that shows the service name. Else just format the uuid. known_uuid_to_string(uuid: &Uuid) -> String187 pub fn known_uuid_to_string(uuid: &Uuid) -> String { 188 if let Some(p) = Self::is_known_profile(uuid) { 189 format!("{}: {:?}", uuid, p) 190 } else { 191 uuid.to_string() 192 } 193 } 194 } 195 196 #[cfg(test)] 197 mod tests { 198 use bt_topshim::btif::Uuid; 199 200 #[test] test_uuidhelper()201 fn test_uuidhelper() { 202 for (uuid, _) in super::PROFILES.iter() { 203 let converted = Uuid::from_string(uuid.to_string()).unwrap(); 204 assert_eq!(*uuid, converted); 205 } 206 } 207 } 208