use std::sync::Arc;
use futures_core::Stream;
use futures_lite::StreamExt;
#[cfg(feature = "l2cap")]
use super::l2cap_channel::{L2capChannelReader, L2capChannelWriter};
use super::DeviceId;
use crate::device::ServicesChanged;
use crate::error::ErrorKind;
use crate::pairing::PairingAgent;
use crate::{btuuid, AdvertisementData, Device, Error, ManufacturerData, Result, Service, Uuid};
#[derive(Debug, Clone)]
pub struct DeviceImpl {
pub(super) inner: Arc<bluer::Device>,
session: Arc<bluer::Session>,
}
impl PartialEq for DeviceImpl {
fn eq(&self, other: &Self) -> bool {
self.inner.adapter_name() == other.inner.adapter_name() && self.inner.address() == other.inner.address()
}
}
impl Eq for DeviceImpl {}
impl std::hash::Hash for DeviceImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.adapter_name().hash(state);
self.inner.address().hash(state);
}
}
impl std::fmt::Display for DeviceImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name().as_deref().unwrap_or("(Unknown)"))
}
}
impl Device {
pub(super) fn new(session: Arc<bluer::Session>, adapter: &bluer::Adapter, addr: bluer::Address) -> Result<Device> {
Ok(Device(DeviceImpl {
inner: Arc::new(adapter.device(addr)?),
session,
}))
}
}
impl DeviceImpl {
pub fn id(&self) -> DeviceId {
DeviceId(self.inner.address())
}
pub fn name(&self) -> Result<String> {
match tokio::runtime::Handle::try_current() {
Ok(handle) => tokio::task::block_in_place(move || handle.block_on(self.name_async())),
Err(_) => tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
.block_on(self.name_async()),
}
}
pub async fn name_async(&self) -> Result<String> {
self.inner.alias().await.map_err(Into::into)
}
pub async fn is_connected(&self) -> bool {
self.inner.is_connected().await.unwrap_or(false)
}
pub async fn is_paired(&self) -> Result<bool> {
self.inner.is_paired().await.map_err(Into::into)
}
pub async fn pair(&self) -> Result<()> {
if self.is_paired().await? {
return Ok(());
}
self.inner.pair().await.map_err(Into::into)
}
pub async fn pair_with_agent<T: PairingAgent + 'static>(&self, agent: &T) -> Result<()> {
if self.is_paired().await? {
return Ok(());
}
let agent = {
let agent: &'static T = unsafe { std::mem::transmute(agent) };
async fn req_device(
session: Arc<bluer::Session>,
adapter: &str,
addr: bluer::Address,
) -> Result<Device, bluer::agent::ReqError> {
let adapter = session.adapter(adapter).map_err(|_| bluer::agent::ReqError::Rejected)?;
let device = adapter.device(addr).map_err(|_| bluer::agent::ReqError::Rejected)?;
Ok(Device(DeviceImpl {
inner: Arc::new(device),
session,
}))
}
bluer::agent::Agent {
request_passkey: Some(Box::new({
let session = self.session.clone();
move |req: bluer::agent::RequestPasskey| {
let session = session.clone();
Box::pin(async move {
let device = req_device(session, &req.adapter, req.device).await?;
match agent.request_passkey(&device).await {
Ok(passkey) => Ok(passkey.into()),
Err(_) => Err(bluer::agent::ReqError::Rejected),
}
})
}
})),
display_passkey: Some(Box::new({
let session = self.session.clone();
move |req: bluer::agent::DisplayPasskey| {
let session = session.clone();
Box::pin(async move {
let device = req_device(session, &req.adapter, req.device).await?;
if let Ok(passkey) = req.passkey.try_into() {
agent.display_passkey(&device, passkey);
Ok(())
} else {
Err(bluer::agent::ReqError::Rejected)
}
})
}
})),
request_confirmation: Some(Box::new({
let session = self.session.clone();
move |req: bluer::agent::RequestConfirmation| {
let session = session.clone();
Box::pin(async move {
let session = session.clone();
let device = req_device(session, &req.adapter, req.device).await?;
if let Ok(passkey) = req.passkey.try_into() {
agent
.confirm_passkey(&device, passkey)
.await
.map_err(|_| bluer::agent::ReqError::Rejected)
} else {
Err(bluer::agent::ReqError::Rejected)
}
})
}
})),
..Default::default()
}
};
let _handle = self.session.register_agent(agent).await?;
self.pair().await
}
pub async fn unpair(&self) -> Result<()> {
if self.is_connected().await {
self.inner.disconnect().await?;
}
let adapter = self.session.adapter(self.inner.adapter_name())?;
adapter.remove_device(self.inner.address()).await.map_err(Into::into)
}
pub async fn discover_services(&self) -> Result<Vec<Service>> {
self.services().await
}
pub async fn discover_services_with_uuid(&self, uuid: Uuid) -> Result<Vec<Service>> {
Ok(self
.services()
.await?
.into_iter()
.filter(|x| x.uuid() == uuid)
.collect())
}
pub async fn services(&self) -> Result<Vec<Service>> {
Ok(self
.inner
.services()
.await?
.into_iter()
.map(|x| Service::new(self.inner.clone(), x))
.collect())
}
pub async fn service_changed_indications(
&self,
) -> Result<impl Stream<Item = Result<ServicesChanged>> + Send + Unpin + '_> {
let mut characteristic = Err(Error::from(ErrorKind::NotFound));
{
let services = self.inner.services().await?;
for service in services {
if service.uuid().await? == btuuid::services::GENERIC_ATTRIBUTE {
for c in service.characteristics().await? {
if c.uuid().await? == btuuid::characteristics::SERVICE_CHANGED {
characteristic = Ok(c);
}
}
}
}
}
let notifications = Box::pin(characteristic?.notify().await?);
Ok(notifications.map(|data| {
if data.len() == 4 {
let start_handle = u16::from_le_bytes(data[..2].try_into().unwrap());
let end_handle = u16::from_le_bytes(data[2..].try_into().unwrap());
Ok(ServicesChanged(ServicesChangedImpl(start_handle..=end_handle)))
} else {
Err(ErrorKind::InvalidParameter.into())
}
}))
}
pub async fn rssi(&self) -> Result<i16> {
Err(ErrorKind::NotSupported.into())
}
pub(super) async fn adv_data(&self) -> AdvertisementData {
let device = &self.inner;
let is_connectable = true;
let local_name = device.alias().await.unwrap_or_default();
let local_name = (!local_name.is_empty()).then_some(local_name);
let manufacturer_data = device
.manufacturer_data()
.await
.unwrap_or_default()
.and_then(|data| data.into_iter().next())
.map(|(company_id, data)| ManufacturerData { company_id, data });
let tx_power_level = device.tx_power().await.unwrap_or_default();
let service_data = device.service_data().await.unwrap_or_default().unwrap_or_default();
let services = device
.uuids()
.await
.unwrap_or_default()
.map_or(Vec::new(), |x| x.into_iter().collect());
AdvertisementData {
local_name,
manufacturer_data,
service_data,
services,
tx_power_level,
is_connectable,
}
}
#[cfg(feature = "l2cap")]
pub async fn open_l2cap_channel(
&self,
_psm: u16,
_secure: bool,
) -> std::prelude::v1::Result<(L2capChannelReader, L2capChannelWriter), crate::Error> {
Err(ErrorKind::NotSupported.into())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ServicesChangedImpl(std::ops::RangeInclusive<u16>);
impl ServicesChangedImpl {
pub fn was_invalidated(&self, service: &Service) -> bool {
let service_id = service.0.inner.id();
self.0.contains(&service_id)
}
}