use std::fmt::Debug;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::{collections::BTreeMap, error::Error, time::Duration};
#[cfg(not(feature = "disable_afit"))]
use async_trait::async_trait;
use bluer::adv::{Advertisement, AdvertisementHandle, Type};
use bluer::{Device, Address};
use futures::executor;
use crate::session::Session;
use crate::util::set_device_addr;
const APPLE_MAGIC: u16 = 0x4c;
pub trait AdvertisableData: Clone + PartialEq + Debug + Sync {
fn octets(&self) -> Vec<u8>;
}
#[cfg_attr(not(feature = "disable_afit"), async_trait)]
pub trait Advertisable<T: AdvertisableData> {
fn validate_user_data(_user_data: &T) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn assemble_advertisement(
session: &mut Session,
user_data: &T,
) -> Result<Advertisement, Box<dyn Error>>;
async fn register(
session: &mut Session,
user_data: &T,
) -> Result<AdvertisementHandle, Box<dyn Error>> {
Self::validate_user_data(user_data)?;
let advertisement = Self::assemble_advertisement(session, user_data)?;
Ok(session.adapter.advertise(advertisement).await?)
}
}
pub enum AdvertisementType {
AirDrop(AirDropAdvertisementData),
AirPlaySource, AirPlayTarget(AirPlayTargetAdvertisementData),
AirPrint(AirPrintAdvertisementData),
FindMy(FindMyAdvertisementData),
}
pub fn get_adv_data_from_device(device: Device) -> Option<AdvertisementType> {
let binding = executor::block_on(device.manufacturer_data()).ok()??;
let manufacturer_data = binding.get(&APPLE_MAGIC)?;
match manufacturer_data[0] {
0x05 => Some(AdvertisementType::AirDrop(
AirDropAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
)),
0x0a => Some(AdvertisementType::AirPlaySource),
0x09 => Some(AdvertisementType::AirPlayTarget(
AirPlayTargetAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
)),
0x03 => Some(AdvertisementType::AirPrint(
AirPrintAdvertisementData::try_from(manufacturer_data.clone()).ok()?,
)),
0x12 => Some(AdvertisementType::FindMy(
FindMyAdvertisementData::try_from((device.address(), manufacturer_data.clone())).ok()?,
)),
_ => None,
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AirDropAdvertisementData {
pub apple_id: [u8; 2],
pub phone: [u8; 2],
pub email: [u8; 2],
}
impl AdvertisableData for AirDropAdvertisementData {
fn octets(&self) -> Vec<u8> {
[
vec![
0x05, 0x12, ],
vec![0; 8], vec![0x01], self.apple_id.to_vec(),
self.phone.to_vec(),
self.email.to_vec(),
self.email.to_vec(),
]
.concat()
}
}
impl TryFrom<Vec<u8>> for AirDropAdvertisementData {
type Error = Box<dyn Error>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(AirDropAdvertisementData {
apple_id: value[11..13].try_into()?,
phone: value[13..15].try_into()?,
email: value[15..17].try_into()?,
})
}
}
pub struct AirDropAdvertisement;
impl Advertisable<AirDropAdvertisementData> for AirDropAdvertisement {
fn assemble_advertisement(
session: &mut Session,
user_data: &AirDropAdvertisementData,
) -> Result<Advertisement, Box<dyn Error>> {
Ok(Advertisement {
advertisement_type: Type::Broadcast,
local_name: Some(session.adapter.name().to_string()),
timeout: Some(Duration::from_millis(0)),
min_interval: Some(Duration::from_millis(100)),
max_interval: Some(Duration::from_millis(200)),
manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
..Default::default()
})
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AirPlaySourceAdvertisementData;
impl AdvertisableData for AirPlaySourceAdvertisementData {
fn octets(&self) -> Vec<u8> {
vec![
0x0a, 0x01, 0x00,
]
}
}
impl TryFrom<Vec<u8>> for AirPlaySourceAdvertisementData {
type Error = Box<dyn Error>;
fn try_from(_value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(AirPlaySourceAdvertisementData {})
}
}
pub struct AirPlaySourceAdvertisement;
impl Advertisable<AirPlaySourceAdvertisementData> for AirPlaySourceAdvertisement {
fn assemble_advertisement(
session: &mut Session,
user_data: &AirPlaySourceAdvertisementData,
) -> Result<Advertisement, Box<dyn Error>> {
Ok(Advertisement {
advertisement_type: Type::Broadcast,
local_name: Some(session.adapter.name().to_string()),
timeout: Some(Duration::from_millis(0)),
min_interval: Some(Duration::from_millis(100)),
max_interval: Some(Duration::from_millis(200)),
manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
..Default::default()
})
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AirPlayTargetAdvertisementData {
pub ip_address: Ipv4Addr,
}
impl AdvertisableData for AirPlayTargetAdvertisementData {
fn octets(&self) -> Vec<u8> {
let ip_address = self.ip_address.octets();
[
vec![
0x09, 0x06, 0x03, 0x07,
],
ip_address.to_vec(),
]
.concat()
}
}
impl TryFrom<Vec<u8>> for AirPlayTargetAdvertisementData {
type Error = Box<dyn Error>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let ip_address: [u8; 4] = value[4..8].try_into()?;
Ok(AirPlayTargetAdvertisementData {
ip_address: Ipv4Addr::from(ip_address),
})
}
}
pub struct AirPlayTargetAdvertisement;
impl Advertisable<AirPlayTargetAdvertisementData> for AirPlayTargetAdvertisement {
fn assemble_advertisement(
session: &mut Session,
user_data: &AirPlayTargetAdvertisementData,
) -> Result<Advertisement, Box<dyn Error>> {
Ok(Advertisement {
advertisement_type: Type::Broadcast,
local_name: Some(session.adapter.name().to_string()),
timeout: Some(Duration::from_millis(0)),
min_interval: Some(Duration::from_millis(100)),
max_interval: Some(Duration::from_millis(200)),
manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
..Default::default()
})
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct AirPrintAdvertisementData {
pub port: u16,
pub ip_addr: Ipv6Addr,
pub power: u8,
}
impl AdvertisableData for AirPrintAdvertisementData {
fn octets(&self) -> Vec<u8> {
let port = self.port.to_be_bytes();
let ip_addr = self.ip_addr.octets();
[
vec![
0x03, 0x16, 0x74, 0x07, 0x6f, ],
port.to_vec(),
ip_addr.to_vec(),
vec![self.power],
]
.concat()
}
}
impl TryFrom<Vec<u8>> for AirPrintAdvertisementData {
type Error = Box<dyn Error>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let ip_address: [u8; 16] = value[7..23].try_into()?;
Ok(AirPrintAdvertisementData {
port: (value[5] as u16) << 8 | value[6] as u16,
ip_addr: Ipv6Addr::from(ip_address),
power: value[23],
})
}
}
pub struct AirPrintAdvertisement;
impl Advertisable<AirPrintAdvertisementData> for AirPrintAdvertisement {
fn assemble_advertisement(
session: &mut Session,
user_data: &AirPrintAdvertisementData,
) -> Result<Advertisement, Box<dyn Error>> {
Ok(Advertisement {
advertisement_type: Type::Broadcast,
local_name: Some(session.adapter.name().to_string()),
timeout: Some(Duration::from_millis(0)),
min_interval: Some(Duration::from_millis(100)),
max_interval: Some(Duration::from_millis(200)),
manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
..Default::default()
})
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct FindMyAdvertisementData {
pub public_key: [u8; 28],
}
impl AdvertisableData for FindMyAdvertisementData {
fn octets(&self) -> Vec<u8> {
let public_key = self.public_key.split_at(6);
[
vec![
0x12, 0x19, 0x00,
],
public_key.1.to_vec(),
vec![public_key.0[0] >> 6],
]
.concat()
}
}
impl TryFrom<(Address, Vec<u8>)> for FindMyAdvertisementData {
type Error = Box<dyn Error>;
fn try_from(value: (Address, Vec<u8>)) -> Result<Self, Self::Error> {
let public_key: [u8; 28] = [&value.0.0, &value.1[2..24]]
.concat()
.as_slice()
.try_into()?;
Ok(FindMyAdvertisementData {
public_key: public_key,
})
}
}
pub struct FindMyAdvertisement;
impl Advertisable<FindMyAdvertisementData> for FindMyAdvertisement {
fn assemble_advertisement(
session: &mut Session,
user_data: &FindMyAdvertisementData,
) -> Result<Advertisement, Box<dyn Error>> {
set_device_addr(session, &user_data.public_key[0..6])?;
Ok(Advertisement {
advertisement_type: Type::Broadcast,
local_name: Some(session.adapter.name().to_string()),
timeout: Some(Duration::from_millis(0)),
min_interval: Some(Duration::from_millis(100)),
max_interval: Some(Duration::from_millis(200)),
manufacturer_data: BTreeMap::from([(APPLE_MAGIC, user_data.octets())]),
..Default::default()
})
}
}