1 //! This library provides helper functions to parse info from advertising data.
2
3 use std::collections::HashMap;
4
5 use bt_topshim::bindings::root::bluetooth::Uuid;
6 use bt_topshim::btif::Uuid128Bit;
7
8 // Advertising data types.
9 const FLAGS: u8 = 0x01;
10 const COMPLETE_LIST_16_BIT_SERVICE_UUIDS: u8 = 0x03;
11 const COMPLETE_LIST_32_BIT_SERVICE_UUIDS: u8 = 0x05;
12 const COMPLETE_LIST_128_BIT_SERVICE_UUIDS: u8 = 0x07;
13 const SHORTENED_LOCAL_NAME: u8 = 0x08;
14 const COMPLETE_LOCAL_NAME: u8 = 0x09;
15 const SERVICE_DATA_16_BIT_UUID: u8 = 0x16;
16 const SERVICE_DATA_32_BIT_UUID: u8 = 0x20;
17 const SERVICE_DATA_128_BIT_UUID: u8 = 0x21;
18 const MANUFACTURER_SPECIFIC_DATA: u8 = 0xff;
19
20 struct AdvDataIterator<'a> {
21 data: &'a [u8],
22 data_type: u8,
23 cur: usize, // to keep current position
24 }
25
26 // Iterates over Advertising Data's elements having the given AD type. `next()`
27 // returns the next slice of the advertising data element excluding the length
28 // and type.
29 impl<'a> Iterator for AdvDataIterator<'a> {
30 type Item = &'a [u8];
next(&mut self) -> Option<&'a [u8]>31 fn next(&mut self) -> Option<&'a [u8]> {
32 let mut i = self.cur;
33 while i < self.data.len() {
34 let len: usize = self.data[i].into();
35 if (len == 0) || (i + len >= self.data.len()) {
36 break;
37 }
38 if self.data[i + 1] == self.data_type {
39 self.cur = i + len + 1;
40 return Some(&self.data[i + 2..self.cur]);
41 }
42 i += len + 1;
43 }
44 None
45 }
46 }
47
iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator48 fn iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator {
49 AdvDataIterator { data, data_type, cur: 0 }
50 }
51
52 // Helper function to extract flags from advertising data
extract_flags(bytes: &[u8]) -> u853 pub fn extract_flags(bytes: &[u8]) -> u8 {
54 iterate_adv_data(bytes, FLAGS).next().map_or(0, |v| v[0])
55 }
56
57 // Helper function to extract service uuids (128bit) from advertising data
extract_service_uuids(bytes: &[u8]) -> Vec<Uuid128Bit>58 pub fn extract_service_uuids(bytes: &[u8]) -> Vec<Uuid128Bit> {
59 iterate_adv_data(bytes, COMPLETE_LIST_16_BIT_SERVICE_UUIDS)
60 .flat_map(|slice| slice.chunks(2))
61 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok().map(|uuid| uuid.uu))
62 .chain(
63 iterate_adv_data(bytes, COMPLETE_LIST_32_BIT_SERVICE_UUIDS)
64 .flat_map(|slice| slice.chunks(4))
65 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok().map(|uuid| uuid.uu)),
66 )
67 .chain(
68 iterate_adv_data(bytes, COMPLETE_LIST_128_BIT_SERVICE_UUIDS)
69 .flat_map(|slice| slice.chunks(16))
70 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok().map(|uuid| uuid.uu)),
71 )
72 .collect()
73 }
74
75 // Helper function to extract name from advertising data
extract_name(bytes: &[u8]) -> String76 pub fn extract_name(bytes: &[u8]) -> String {
77 iterate_adv_data(bytes, COMPLETE_LOCAL_NAME)
78 .next()
79 .or(iterate_adv_data(bytes, SHORTENED_LOCAL_NAME).next())
80 .map_or("".to_string(), |v| String::from_utf8_lossy(v).to_string())
81 }
82
83 // Helper function to extract service data from advertising data
extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>>84 pub fn extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>> {
85 iterate_adv_data(bytes, SERVICE_DATA_16_BIT_UUID)
86 .filter_map(|slice| {
87 Uuid::try_from_little_endian(slice.get(0..2)?)
88 .ok()
89 .map(|uuid| (uuid.to_string(), slice[2..].to_vec()))
90 })
91 .chain(iterate_adv_data(bytes, SERVICE_DATA_32_BIT_UUID).filter_map(|slice| {
92 Uuid::try_from_little_endian(slice.get(0..4)?)
93 .ok()
94 .map(|uuid| (uuid.to_string(), slice[4..].to_vec()))
95 }))
96 .chain(iterate_adv_data(bytes, SERVICE_DATA_128_BIT_UUID).filter_map(|slice| {
97 Uuid::try_from_little_endian(slice.get(0..16)?)
98 .ok()
99 .map(|uuid| (uuid.to_string(), slice[16..].to_vec()))
100 }))
101 .collect()
102 }
103
104 // Helper function to extract manufacturer data from advertising data
extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>>105 pub fn extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>> {
106 iterate_adv_data(bytes, MANUFACTURER_SPECIFIC_DATA)
107 .filter_map(|slice| {
108 slice.get(0..2)?.try_into().ok().map(|be| (u16::from_be_bytes(be), slice[2..].to_vec()))
109 })
110 .collect()
111 }
112
113 #[cfg(test)]
114 mod tests {
115 use super::*;
116
117 #[test]
test_extract_flags()118 fn test_extract_flags() {
119 let payload: Vec<u8> = vec![
120 2,
121 FLAGS,
122 3,
123 17,
124 COMPLETE_LIST_128_BIT_SERVICE_UUIDS,
125 0,
126 1,
127 2,
128 3,
129 4,
130 5,
131 6,
132 7,
133 8,
134 9,
135 10,
136 11,
137 12,
138 13,
139 14,
140 15,
141 ];
142 let flags = extract_flags(payload.as_slice());
143 assert_eq!(flags, 3);
144 }
145
146 #[test]
test_extract_service_uuids()147 fn test_extract_service_uuids() {
148 let payload: Vec<u8> = vec![2, FLAGS, 3];
149 let uuids = extract_service_uuids(payload.as_slice());
150 assert_eq!(uuids.len(), 0);
151
152 let payload: Vec<u8> = vec![
153 2,
154 FLAGS,
155 3,
156 3,
157 COMPLETE_LIST_16_BIT_SERVICE_UUIDS,
158 0x2C,
159 0xFE,
160 5,
161 COMPLETE_LIST_32_BIT_SERVICE_UUIDS,
162 2,
163 3,
164 4,
165 5,
166 17,
167 COMPLETE_LIST_128_BIT_SERVICE_UUIDS,
168 0,
169 1,
170 2,
171 3,
172 4,
173 5,
174 6,
175 7,
176 8,
177 9,
178 10,
179 11,
180 12,
181 13,
182 14,
183 15,
184 ];
185 let uuids = extract_service_uuids(payload.as_slice());
186 assert_eq!(uuids.len(), 3);
187 assert_eq!(
188 uuids[0],
189 Uuid::from([
190 0x0, 0x0, 0xFE, 0x2C, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34,
191 0xfb
192 ])
193 .uu
194 );
195 assert_eq!(
196 uuids[1],
197 Uuid::from([
198 0x5, 0x4, 0x3, 0x2, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34,
199 0xfb
200 ])
201 .uu
202 );
203 assert_eq!(uuids[2], Uuid::from([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]).uu);
204 }
205
206 #[test]
test_extract_name()207 fn test_extract_name() {
208 let payload: Vec<u8> = vec![2, FLAGS, 3];
209 let name = extract_name(payload.as_slice());
210 assert_eq!(name, "");
211
212 let payload: Vec<u8> = vec![2, FLAGS, 3, 5, COMPLETE_LOCAL_NAME, 116, 101, 115, 116];
213 let name = extract_name(payload.as_slice());
214 assert_eq!(name, "test");
215
216 let payload: Vec<u8> = vec![2, FLAGS, 3, 5, SHORTENED_LOCAL_NAME, 116, 101, 115, 116];
217 let name = extract_name(payload.as_slice());
218 assert_eq!(name, "test");
219 }
220
221 #[test]
test_extract_service_data()222 fn test_extract_service_data() {
223 let payload: Vec<u8> = vec![2, FLAGS, 3];
224 let service_data = extract_service_data(payload.as_slice());
225 assert_eq!(service_data.len(), 0);
226
227 let payload: Vec<u8> = vec![
228 4,
229 SERVICE_DATA_16_BIT_UUID,
230 0x2C,
231 0xFE,
232 0xFF,
233 6,
234 SERVICE_DATA_32_BIT_UUID,
235 2,
236 3,
237 4,
238 5,
239 0xFE,
240 18,
241 SERVICE_DATA_128_BIT_UUID,
242 0,
243 1,
244 2,
245 3,
246 4,
247 5,
248 6,
249 7,
250 8,
251 9,
252 10,
253 11,
254 12,
255 13,
256 14,
257 15,
258 16,
259 17,
260 SERVICE_DATA_128_BIT_UUID,
261 1,
262 2,
263 3,
264 4,
265 5,
266 6,
267 7,
268 8,
269 9,
270 10,
271 11,
272 12,
273 13,
274 14,
275 15,
276 16,
277 ];
278 let service_data = extract_service_data(payload.as_slice());
279 assert_eq!(service_data.len(), 4);
280 let expected_uuid = Uuid::from([
281 0x0, 0x0, 0xFE, 0x2C, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
282 ])
283 .to_string();
284 assert_eq!(service_data.get(&expected_uuid), Some(&vec![0xFF]));
285 let expected_uuid = Uuid::from([
286 0x5, 0x4, 0x3, 0x2, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
287 ])
288 .to_string();
289 assert_eq!(service_data.get(&expected_uuid), Some(&vec![0xFE]));
290 let expected_uuid =
291 Uuid::from([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]).to_string();
292 assert_eq!(service_data.get(&expected_uuid), Some(&vec![16]));
293 let expected_uuid =
294 Uuid::from([16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).to_string();
295 assert_eq!(service_data.get(&expected_uuid), Some(&vec![]));
296 }
297
298 #[test]
test_extract_manufacturer_data()299 fn test_extract_manufacturer_data() {
300 let payload: Vec<u8> = vec![2, FLAGS, 3];
301 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
302 assert_eq!(manufacturer_data.len(), 0);
303
304 let payload: Vec<u8> = vec![2, MANUFACTURER_SPECIFIC_DATA, 0];
305 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
306 assert_eq!(manufacturer_data.len(), 0);
307
308 let payload: Vec<u8> =
309 vec![4, MANUFACTURER_SPECIFIC_DATA, 0, 1, 2, 3, MANUFACTURER_SPECIFIC_DATA, 1, 2];
310 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
311 assert_eq!(manufacturer_data.len(), 2);
312 assert_eq!(manufacturer_data.get(&1), Some(&vec![2]));
313 assert_eq!(manufacturer_data.get(&258), Some(&vec![]));
314 }
315 }
316