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;
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 lazy_static! {
89     // AVRCP fights with A2DP when initializing, so let's initiate profiles in a known good order.
90     // Specifically, A2DP must be initialized before AVRCP.
91     // TODO (b/286991526): remove after issue is resolved
92     static ref ORDERED_SUPPORTED_PROFILES: Vec<Profile> = 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 lazy_static! {
113     static ref SUPPORTED_PROFILES: HashSet<Profile> =
114         ORDERED_SUPPORTED_PROFILES.iter().cloned().collect();
115 }
116 
117 lazy_static! {
118     static ref PROFILES: HashMap<Uuid, Profile> = [
119         (Uuid::from_string(A2DP_SINK).unwrap(), Profile::A2dpSink),
120         (Uuid::from_string(A2DP_SOURCE).unwrap(), Profile::A2dpSource),
121         (Uuid::from_string(ADV_AUDIO_DIST).unwrap(), Profile::AdvAudioDist),
122         (Uuid::from_string(BAS).unwrap(), Profile::Bas),
123         (Uuid::from_string(DIS).unwrap(), Profile::Dis),
124         (Uuid::from_string(HSP).unwrap(), Profile::Hsp),
125         (Uuid::from_string(HSP_AG).unwrap(), Profile::HspAg),
126         (Uuid::from_string(HFP).unwrap(), Profile::Hfp),
127         (Uuid::from_string(HFP_AG).unwrap(), Profile::HfpAg),
128         (Uuid::from_string(AVRCP_CONTROLLER).unwrap(), Profile::AvrcpController),
129         (Uuid::from_string(AVRCP_TARGET).unwrap(), Profile::AvrcpTarget),
130         (Uuid::from_string(OBEX_OBJECT_PUSH).unwrap(), Profile::ObexObjectPush),
131         (Uuid::from_string(HID).unwrap(), Profile::Hid),
132         (Uuid::from_string(HOGP).unwrap(), Profile::Hogp),
133         (Uuid::from_string(PANU).unwrap(), Profile::Panu),
134         (Uuid::from_string(NAP).unwrap(), Profile::Nap),
135         (Uuid::from_string(BNEP).unwrap(), Profile::Bnep),
136         (Uuid::from_string(PBAP_PCE).unwrap(), Profile::PbapPce),
137         (Uuid::from_string(PBAP_PSE).unwrap(), Profile::PbapPse),
138         (Uuid::from_string(MAP).unwrap(), Profile::Map),
139         (Uuid::from_string(MNS).unwrap(), Profile::Mns),
140         (Uuid::from_string(MAS).unwrap(), Profile::Mas),
141         (Uuid::from_string(SAP).unwrap(), Profile::Sap),
142         (Uuid::from_string(HEARING_AID).unwrap(), Profile::HearingAid),
143         (Uuid::from_string(LE_AUDIO).unwrap(), Profile::LeAudio),
144         (Uuid::from_string(DIP).unwrap(), Profile::Dip),
145         (Uuid::from_string(VOLUME_CONTROL).unwrap(), Profile::VolumeControl),
146         (Uuid::from_string(GENERIC_MEDIA_CONTROL).unwrap(), Profile::GenericMediaControl),
147         (Uuid::from_string(MEDIA_CONTROL).unwrap(), Profile::MediaControl),
148         (Uuid::from_string(COORDINATED_SET).unwrap(), Profile::CoordinatedSet),
149     ]
150     .iter()
151     .cloned()
152     .collect();
153 }
154 
155 lazy_static! {
156     static ref PROFILES_UUIDS: HashMap<Profile, Uuid> =
157         PROFILES.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
158 }
159 
160 impl UuidHelper {
161     /// Checks whether a UUID corresponds to a currently enabled profile.
is_profile_supported(profile: &Profile) -> bool162     pub fn is_profile_supported(profile: &Profile) -> bool {
163         SUPPORTED_PROFILES.contains(profile)
164     }
165 
166     /// Converts a UUID to a known profile enum.
is_known_profile(uuid: &Uuid) -> Option<Profile>167     pub fn is_known_profile(uuid: &Uuid) -> Option<Profile> {
168         PROFILES.get(uuid).cloned()
169     }
170 
171     // AVRCP fights with A2DP when initializing, so let's initiate profiles in a known good order.
172     // TODO (b/286991526): remove after issue is resolved
get_ordered_supported_profiles() -> Vec<Profile>173     pub fn get_ordered_supported_profiles() -> Vec<Profile> {
174         ORDERED_SUPPORTED_PROFILES.clone()
175     }
176 
get_supported_profiles() -> HashSet<Profile>177     pub fn get_supported_profiles() -> HashSet<Profile> {
178         SUPPORTED_PROFILES.clone()
179     }
180 
181     /// Converts a profile enum to its UUID if known.
get_profile_uuid(profile: &Profile) -> Option<&Uuid>182     pub fn get_profile_uuid(profile: &Profile) -> Option<&Uuid> {
183         PROFILES_UUIDS.get(profile)
184     }
185 
186     /// If a uuid is known to be a certain service, convert it into a formatted
187     /// string that shows the service name. Else just format the uuid.
known_uuid_to_string(uuid: &Uuid) -> String188     pub fn known_uuid_to_string(uuid: &Uuid) -> String {
189         if let Some(p) = Self::is_known_profile(uuid) {
190             format!("{}: {:?}", uuid.to_string(), p)
191         } else {
192             uuid.to_string()
193         }
194     }
195 }
196 
197 #[cfg(test)]
198 mod tests {
199     use bt_topshim::btif::Uuid;
200 
201     #[test]
test_uuidhelper()202     fn test_uuidhelper() {
203         for (uuid, _) in super::PROFILES.iter() {
204             let converted = Uuid::from_string(uuid.to_string()).unwrap();
205             assert_eq!(*uuid, converted);
206         }
207     }
208 }
209