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