1use std::fmt::Debug;
2use std::net::{Ipv4Addr, Ipv6Addr};
3use std::{collections::BTreeMap, error::Error, time::Duration};
4
5#[cfg(not(feature = "disable_afit"))]
6use async_trait::async_trait;
7use bluer::adv::{Advertisement, AdvertisementHandle, Type};
8use bluer::{Device, Address};
9use futures::executor;
10
11use crate::session::Session;
12use crate::util::set_device_addr;
13
14const APPLE_MAGIC: u16 = 0x4c;
15
16pub trait AdvertisableData: Clone + PartialEq + Debug + Sync {
17 fn octets(&self) -> Vec<u8>;
18}
19
20#[cfg_attr(not(feature = "disable_afit"), async_trait)]
22pub trait Advertisable<T: AdvertisableData> {
24 fn validate_user_data(_user_data: &T) -> Result<(), Box<dyn Error>> {
26 Ok(())
27 }
28 fn assemble_advertisement(
30 session: &mut Session,
31 user_data: &T,
32 ) -> Result<Advertisement, Box<dyn Error>>;
33 async fn register(
35 session: &mut Session,
36 user_data: &T,
37 ) -> Result<AdvertisementHandle, Box<dyn Error>> {
38 Self::validate_user_data(user_data)?;
39 let advertisement = Self::assemble_advertisement(session, user_data)?;
40 Ok(session.adapter.advertise(advertisement).await?)
41 }
42}
43pub enum AdvertisementType {
44 AirDrop(AirDropAdvertisementData),
45 AirPlaySource, AirPlayTarget(AirPlayTargetAdvertisementData),
47 AirPrint(AirPrintAdvertisementData),
48 FindMy(FindMyAdvertisementData),
49}
50pub fn get_adv_data_from_device(device: Device) -> Option<AdvertisementType> {
51 let binding = executor::block_on(device.manufacturer_data()).ok()??;
52 let manufacturer_data = binding.get(&APPLE_MAGIC)?;
53 match manufacturer_data[0] {
54 0x05 => Some(AdvertisementType::AirDrop(
55 AirDropAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
56 )),
57 0x0a => Some(AdvertisementType::AirPlaySource),
58 0x09 => Some(AdvertisementType::AirPlayTarget(
59 AirPlayTargetAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
60 )),
61 0x03 => Some(AdvertisementType::AirPrint(
62 AirPrintAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
63 )),
64 0x12 => Some(AdvertisementType::FindMy(
65 FindMyAdvertisementData::try_from((device.address(), manufacturer_data.clone())).ok()?,
66 )),
67 _ => None,
68 }
69}
70
71#[derive(Clone, PartialEq, Debug)]
73pub struct AirDropAdvertisementData {
74 pub apple_id: [u8; 2],
75 pub phone: [u8; 2],
76 pub email: [u8; 2],
77}
78impl AdvertisableData for AirDropAdvertisementData {
79 fn octets(&self) -> Vec<u8> {
80 [
81 vec![
82 0x05, 0x12, ],
85 vec![0; 8], vec![0x01], self.apple_id.to_vec(),
88 self.phone.to_vec(),
89 self.email.to_vec(),
90 self.email.to_vec(),
91 ]
92 .concat()
93 }
94}
95impl TryFrom<Vec<u8>> for AirDropAdvertisementData {
96 type Error = Box<dyn Error>;
97 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
98 Ok(AirDropAdvertisementData {
99 apple_id: value[11..13].try_into()?,
100 phone: value[13..15].try_into()?,
101 email: value[15..17].try_into()?,
102 })
103 }
104}
105
106pub struct AirDropAdvertisement;
108impl Advertisable<AirDropAdvertisementData> for AirDropAdvertisement {
109 fn assemble_advertisement(
110 session: &mut Session,
111 user_data: &AirDropAdvertisementData,
112 ) -> Result<Advertisement, Box<dyn Error>> {
113 Ok(Advertisement {
114 advertisement_type: Type::Broadcast,
115 local_name: Some(session.adapter.name().to_string()),
116 timeout: Some(Duration::from_millis(0)),
117 min_interval: Some(Duration::from_millis(100)),
118 max_interval: Some(Duration::from_millis(200)),
119 manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
120 ..Default::default()
121 })
122 }
123}
124
125#[derive(Clone, PartialEq, Debug)]
127pub struct AirPlaySourceAdvertisementData;
128impl AdvertisableData for AirPlaySourceAdvertisementData {
129 fn octets(&self) -> Vec<u8> {
130 vec![
132 0x0a, 0x01, 0x00,
135 ]
136 }
137}
138impl TryFrom<Vec<u8>> for AirPlaySourceAdvertisementData {
139 type Error = Box<dyn Error>;
140 fn try_from(_value: Vec<u8>) -> Result<Self, Self::Error> {
141 Ok(AirPlaySourceAdvertisementData {})
142 }
143}
144
145pub struct AirPlaySourceAdvertisement;
147impl Advertisable<AirPlaySourceAdvertisementData> for AirPlaySourceAdvertisement {
148 fn assemble_advertisement(
150 session: &mut Session,
151 user_data: &AirPlaySourceAdvertisementData,
152 ) -> Result<Advertisement, Box<dyn Error>> {
153 Ok(Advertisement {
154 advertisement_type: Type::Broadcast,
155 local_name: Some(session.adapter.name().to_string()),
156 timeout: Some(Duration::from_millis(0)),
157 min_interval: Some(Duration::from_millis(100)),
158 max_interval: Some(Duration::from_millis(200)),
159 manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
160 ..Default::default()
161 })
162 }
163}
164
165#[derive(Clone, PartialEq, Debug)]
167pub struct AirPlayTargetAdvertisementData {
168 pub ip_address: Ipv4Addr,
169}
170impl AdvertisableData for AirPlayTargetAdvertisementData {
171 fn octets(&self) -> Vec<u8> {
172 let ip_address = self.ip_address.octets();
173 [
174 vec![
175 0x09, 0x06, 0x03, 0x07,
178 ],
179 ip_address.to_vec(),
180 ]
181 .concat()
182 }
183}
184impl TryFrom<Vec<u8>> for AirPlayTargetAdvertisementData {
185 type Error = Box<dyn Error>;
186 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
187 let ip_address: [u8; 4] = value[4..8].try_into()?;
188 Ok(AirPlayTargetAdvertisementData {
189 ip_address: Ipv4Addr::from(ip_address),
190 })
191 }
192}
193
194pub struct AirPlayTargetAdvertisement;
196impl Advertisable<AirPlayTargetAdvertisementData> for AirPlayTargetAdvertisement {
197 fn assemble_advertisement(
198 session: &mut Session,
199 user_data: &AirPlayTargetAdvertisementData,
200 ) -> Result<Advertisement, Box<dyn Error>> {
201 Ok(Advertisement {
202 advertisement_type: Type::Broadcast,
203 local_name: Some(session.adapter.name().to_string()),
204 timeout: Some(Duration::from_millis(0)),
205 min_interval: Some(Duration::from_millis(100)),
206 max_interval: Some(Duration::from_millis(200)),
207 manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
208 ..Default::default()
209 })
210 }
211}
212
213#[derive(Clone, PartialEq, Debug)]
215pub struct AirPrintAdvertisementData {
216 pub port: u16,
217 pub ip_addr: Ipv6Addr,
218 pub power: u8,
219}
220impl AdvertisableData for AirPrintAdvertisementData {
221 fn octets(&self) -> Vec<u8> {
222 let port = self.port.to_be_bytes();
223 let ip_addr = self.ip_addr.octets();
224 [
225 vec![
226 0x03, 0x16, 0x74, 0x07, 0x6f, ],
232 port.to_vec(),
233 ip_addr.to_vec(),
234 vec![self.power],
235 ]
236 .concat()
237 }
238}
239impl TryFrom<Vec<u8>> for AirPrintAdvertisementData {
240 type Error = Box<dyn Error>;
241 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
242 let ip_address: [u8; 16] = value[7..23].try_into()?;
243 Ok(AirPrintAdvertisementData {
244 port: (value[5] as u16) << 8 | value[6] as u16,
245 ip_addr: Ipv6Addr::from(ip_address),
246 power: value[23],
247 })
248 }
249}
250
251pub struct AirPrintAdvertisement;
253impl Advertisable<AirPrintAdvertisementData> for AirPrintAdvertisement {
254 fn assemble_advertisement(
255 session: &mut Session,
256 user_data: &AirPrintAdvertisementData,
257 ) -> Result<Advertisement, Box<dyn Error>> {
258 Ok(Advertisement {
259 advertisement_type: Type::Broadcast,
260 local_name: Some(session.adapter.name().to_string()),
261 timeout: Some(Duration::from_millis(0)),
262 min_interval: Some(Duration::from_millis(100)),
263 max_interval: Some(Duration::from_millis(200)),
264 manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
265 ..Default::default()
266 })
267 }
268}
269
270#[derive(Clone, PartialEq, Debug)]
272pub struct FindMyAdvertisementData {
273 pub public_key: [u8; 28],
274}
275impl AdvertisableData for FindMyAdvertisementData {
276 fn octets(&self) -> Vec<u8> {
277 let public_key = self.public_key.split_at(6);
278 [
279 vec![
280 0x12, 0x19, 0x00,
283 ],
284 public_key.1.to_vec(),
285 vec![public_key.0[0] >> 6],
286 ]
287 .concat()
288 }
289}
290impl TryFrom<(Address, Vec<u8>)> for FindMyAdvertisementData {
291 type Error = Box<dyn Error>;
292 fn try_from(value: (Address, Vec<u8>)) -> Result<Self, Self::Error> {
293 let public_key: [u8; 28] = [&value.0.0, &value.1[2..24]]
294 .concat()
295 .as_slice()
296 .try_into()?;
297 Ok(FindMyAdvertisementData {
298 public_key: public_key,
299 })
300 }
301}
302
303pub struct FindMyAdvertisement;
305impl Advertisable<FindMyAdvertisementData> for FindMyAdvertisement {
306 fn assemble_advertisement(
307 session: &mut Session,
308 user_data: &FindMyAdvertisementData,
309 ) -> Result<Advertisement, Box<dyn Error>> {
310 set_device_addr(session, &user_data.public_key[0..6])?;
311 Ok(Advertisement {
312 advertisement_type: Type::Broadcast,
313 local_name: Some(session.adapter.name().to_string()),
314 timeout: Some(Duration::from_millis(0)),
315 min_interval: Some(Duration::from_millis(100)),
316 max_interval: Some(Duration::from_millis(200)),
317 manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
318 ..Default::default()
319 })
320 }
321}