Skip to content

Commit

Permalink
# This is a combination of 62 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

BLE Passive Scanning

# This is the commit message #2:

monitor

# This is the commit message bluez#3:

monitor

# This is the commit message bluez#4:

monitor

# This is the commit message bluez#5:

monitor

# This is the commit message bluez#6:

monitor

# This is the commit message bluez#7:

monitor

# This is the commit message bluez#8:

monitor

# This is the commit message bluez#9:

monitor

# This is the commit message bluez#10:

monitor

# This is the commit message bluez#11:

monitor

# This is the commit message bluez#12:

monitor

# This is the commit message bluez#13:

monitor

# This is the commit message bluez#14:

monitor

# This is the commit message bluez#15:

monitor

# This is the commit message bluez#16:

monitor

# This is the commit message bluez#17:

monitor

# This is the commit message bluez#18:

monitor

# This is the commit message bluez#19:

monitor

# This is the commit message bluez#20:

monitor

# This is the commit message bluez#21:

monitor

# This is the commit message bluez#22:

monitor

# This is the commit message bluez#23:

monitor

# This is the commit message bluez#24:

monitor

# This is the commit message bluez#25:

monitor

# This is the commit message bluez#26:

monitor

# This is the commit message bluez#27:

monitor

# This is the commit message bluez#28:

monitor

# This is the commit message bluez#29:

monitor

# This is the commit message bluez#30:

monitor

# This is the commit message bluez#31:

monitor

# This is the commit message bluez#32:

monitor

# This is the commit message bluez#33:

monitor

# This is the commit message bluez#34:

monitor

# This is the commit message bluez#35:

monitor

# This is the commit message bluez#36:

monitor

# This is the commit message bluez#37:

monitor

# This is the commit message bluez#38:

monitor

# This is the commit message bluez#39:

monitor

# This is the commit message bluez#40:

monitor

# This is the commit message bluez#41:

monitor

# This is the commit message bluez#42:

monitor

# This is the commit message bluez#43:

monitor

# This is the commit message bluez#44:

monitor

# This is the commit message bluez#45:

monitor

# This is the commit message bluez#46:

monitor

# This is the commit message bluez#47:

monitor

# This is the commit message bluez#48:

monitor

# This is the commit message bluez#49:

monitor

# This is the commit message bluez#50:

monitor

# This is the commit message bluez#51:

monitor

# This is the commit message bluez#52:

monitor

# This is the commit message bluez#53:

monitor

# This is the commit message bluez#54:

monitor

# This is the commit message bluez#55:

monitor

# This is the commit message bluez#56:

monitor

# This is the commit message bluez#57:

monitor

# This is the commit message bluez#58:

monitor

# This is the commit message bluez#59:

monitor

# This is the commit message bluez#60:

monitor

# This is the commit message bluez#61:

monitor

# This is the commit message bluez#62:

monitor
  • Loading branch information
otaviojr committed May 10, 2023
1 parent 8ffd4ae commit 71c8ca7
Show file tree
Hide file tree
Showing 4 changed files with 344 additions and 0 deletions.
7 changes: 7 additions & 0 deletions bluer/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::{
device::Device,
gatt, Address, AddressType, Error, ErrorKind, Event, InternalErrorKind, Modalias, Result, SessionInner,
SingleSessionToken, SERVICE_NAME, TIMEOUT,
monitor::{Monitor, RegisteredMonitor, MonitorHandle},
};

pub(crate) const INTERFACE: &str = "org.bluez.Adapter1";
Expand Down Expand Up @@ -109,6 +110,12 @@ impl Adapter {
Ok(addrs)
}

pub async fn register_monitor(&self, monitor: Monitor) -> Result<MonitorHandle> {
let reg_monitor = RegisteredMonitor::new(monitor);
reg_monitor.register(self.inner.clone(), self.name()).await
}


/// Get interface to Bluetooth device of specified address.
pub fn device(&self, address: Address) -> Result<Device> {
Device::new(self.inner.clone(), self.name.clone(), address)
Expand Down
1 change: 1 addition & 0 deletions bluer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ pub mod adv;
#[cfg(feature = "bluetoothd")]
#[cfg_attr(docsrs, doc(cfg(feature = "bluetoothd")))]
pub mod agent;
pub mod monitor;
#[cfg(feature = "bluetoothd")]
mod device;
#[cfg(feature = "bluetoothd")]
Expand Down
332 changes: 332 additions & 0 deletions bluer/src/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
//! Bluetooth monitor agent.

use dbus::{
nonblock::{Proxy, SyncConnection},
Path,
};

use dbus_crossroads::{Crossroads, IfaceBuilder, IfaceToken};
use futures::{pin_mut, Future};
use std::{fmt, pin::Pin, sync::Arc};
use strum::IntoStaticStr;
use tokio::{
select,
sync::{oneshot, Mutex},
};
use uuid::Uuid;

use crate::{method_call, Address, Device, Result, SessionInner, ERR_PREFIX, SERVICE_NAME, TIMEOUT};

pub(crate) const INTERFACE: &str = "org.bluez.AdvertisementMonitor1";
pub(crate) const MANAGER_INTERFACE: &str = "org.bluez.AdvertisementMonitorManager1";
pub(crate) const MANAGER_PATH: &str = "/org/bluez";

// Error response from us to a Bluetooth agent request.
#[derive(Clone, Copy, Debug, displaydoc::Display, Eq, PartialEq, Ord, PartialOrd, Hash, IntoStaticStr)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ReqError {
/// Request was rejected.
Rejected,
/// Request was canceled.
Canceled,
}

impl std::error::Error for ReqError {}

impl Default for ReqError {
fn default() -> Self {
Self::Canceled
}
}

impl From<ReqError> for dbus::MethodErr {
fn from(err: ReqError) -> Self {
let name: &'static str = err.into();
Self::from((ERR_PREFIX.to_string() + name, &err.to_string()))
}
}

/// Result of a Bluetooth agent request to us.
pub type ReqResult<T> = std::result::Result<T, ReqError>;

pub type ReleaseFn =
Box<dyn (Fn() -> Pin<Box<dyn Future<Output = ReqResult<()>> + Send>>) + Send + Sync>;

pub type ActivateFn =
Box<dyn (Fn() -> Pin<Box<dyn Future<Output = ReqResult<()>> + Send>>) + Send + Sync>;

#[derive(Debug)]
#[non_exhaustive]
pub struct DeviceFound {
/// Adapter making the request.
pub adapter: String,
/// Address of device making the request.
pub addr: Address,
}

pub type DeviceFoundFn =
Box<dyn (Fn(DeviceFound) -> Pin<Box<dyn Future<Output = ReqResult<String>> + Send>>) + Send + Sync>;

#[derive(Debug)]
#[non_exhaustive]
pub struct DeviceLost {
/// Adapter making the request.
pub adapter: String,
/// Address of device making the request.
pub addr: Address,
}

pub type DeviceLostFn =
Box<dyn (Fn(DeviceLost) -> Pin<Box<dyn Future<Output = ReqResult<String>> + Send>>) + Send + Sync>;

pub struct MonitorCallbacks {
pub release: Option<ReleaseFn>,
pub activate: Option<ActivateFn>,
pub device_found: Option<DeviceFoundFn>,
pub device_lost: Option<DeviceLostFn>,
}

impl Default for MonitorCallbacks {
fn default() -> MonitorCallbacks {
MonitorCallbacks {
release: Option::None,
activate: Option::None,
device_found: Option::None,
device_lost: Option::None,
}
}
}

pub struct Monitor {
inner: Arc<SessionInner>,
dbus_path: Path<'static>,
callbacks: MonitorCallbacks
}


impl Monitor {

pub(crate) fn new(inner: Arc<SessionInner>, callbacks: MonitorCallbacks) -> Self {
let name = dbus::Path::new(format!("{}/{}", MANAGER_PATH,Uuid::new_v4().as_simple())).unwrap();
Self { inner: inner, callbacks: callbacks, dbus_path: name }
}

fn proxy(&self) -> Proxy<'_, &SyncConnection> {
Proxy::new(SERVICE_NAME, self.dbus_path.clone(), TIMEOUT, &*self.inner.connection)
}

dbus_interface!();
dbus_default_interface!(INTERFACE);
}

pub(crate) struct RegisteredMonitor {
m: Monitor,
cancel: Mutex<Option<oneshot::Sender<()>>>,
}

impl RegisteredMonitor {
pub(crate) fn new(monitor: Monitor) -> Self {
Self { m: monitor, cancel: Mutex::new(None) }
}

async fn get_cancel(&self) -> oneshot::Receiver<()> {
let (cancel_tx, cancel_rx) = oneshot::channel();
*self.cancel.lock().await = Some(cancel_tx);
cancel_rx
}

async fn call<A, F, R>(&self, f: &Option<impl Fn(A) -> F>, arg: A) -> ReqResult<R>
where
F: Future<Output = ReqResult<R>> + Send + 'static,
{
match f {
Some(f) => f(arg).await,
None => Err(ReqError::Rejected),
}
}

async fn call_no_params<F, R>(&self, f: &Option<impl Fn() -> F>) -> ReqResult<R>
where
F: Future<Output = ReqResult<R>> + Send + 'static,
{
match f {
Some(f) => f().await,
None => Err(ReqError::Rejected),
}
}

fn parse_device_path(device: &dbus::Path<'static>) -> ReqResult<(String, Address)> {
match Device::parse_dbus_path(device) {
Some((adapter, addr)) => Ok((adapter.to_string(), addr)),
None => {
log::error!("Cannot parse device path {}", &device);
Err(ReqError::Rejected)
}
}
}

pub(crate) fn register_interface(cr: &mut Crossroads) -> IfaceToken<Arc<Self>> {
cr.register(INTERFACE, |ib: &mut IfaceBuilder<Arc<Self>>| {
ib.method_with_cr_async(
"Release",
(),
(),
|ctx, cr, ()| {
method_call(ctx, cr, |reg: Arc<Self>| async move {
reg.call_no_params(&reg.m.callbacks.release,).await?;
Ok(())
})
},
);
ib.method_with_cr_async(
"Activate",
(),
(),
|ctx, cr, ()| {
method_call(ctx, cr, |reg: Arc<Self>| async move {
reg.call_no_params(
&reg.m.callbacks.activate, )
.await?;
Ok(())
})
},
);
ib.method_with_cr_async(
"DeviceFound",
("device",),
(),
|ctx, cr, (addr,):(dbus::Path<'static>,) | {
method_call(ctx, cr, |reg: Arc<Self>| async move {
let (adapter, addr) = Self::parse_device_path(&addr)?;
reg.call(&reg.m.callbacks.device_found, DeviceFound { adapter, addr },)
.await?;
Ok(())
})
},
);
ib.method_with_cr_async(
"DeviceLost",
("device",),
(),
|ctx, cr, (addr,): (dbus::Path<'static>,) | {
method_call(ctx, cr, move |reg: Arc<Self>| async move {
let (adapter, addr) = Self::parse_device_path(&addr)?;
reg.call(
&reg.m.callbacks.device_lost,
DeviceLost { adapter, addr },
)
.await?;
Ok(())
})
},
);
})
}

pub(crate) async fn register(self, inner: Arc<SessionInner>, adapter_name: &str) -> Result<MonitorHandle> {
let manager_path = dbus::Path::new(format!("{}/{}", MANAGER_PATH, adapter_name)).unwrap();
let name = self.m.dbus_path.clone();

log::trace!("Publishing monitor at {}", &name);

{
let mut cr = inner.crossroads.lock().await;
cr.insert(name.clone(), &[inner.monitor_token], Arc::new(self));
}

log::trace!("Registering monitor at {}", &name);
let proxy = Proxy::new(SERVICE_NAME, manager_path, TIMEOUT, inner.connection.clone());
proxy.method_call(MANAGER_INTERFACE, "RegisterMonitor", (name.clone(),)).await?;

let (drop_tx, drop_rx) = oneshot::channel();
let unreg_name = name.clone();
tokio::spawn(async move {
let _ = drop_rx.await;

log::trace!("Unregistering monitor at {}", &unreg_name);
let _: std::result::Result<(), dbus::Error> =
proxy.method_call(MANAGER_INTERFACE, "UnregisterMonitor", (unreg_name.clone(),)).await;

log::trace!("Unpublishing monitor at {}", &unreg_name);
let mut cr = inner.crossroads.lock().await;
let _: Option<Self> = cr.remove(&unreg_name);
});

Ok(MonitorHandle { name, _drop_tx: drop_tx })
}
}

define_properties!(
Monitor,
/// Bluetooth monitor properties.
pub ManitorProperties => {

// ===========================================================================================
// Monitor properties
// ===========================================================================================

property(
MonitorType, String,
dbus: (INTERFACE, "Type", String, MANDATORY),
get: (monitor_type, v => { v.to_owned() }),
);

property(
RSSILowThreshold, i16,
dbus: (INTERFACE, "RSSILowThreshold", i16, OPTIONAL),
get: (rssi_low_threshold, v => {v.to_owned()}),
);

property(
RSSIHighThreshold, i16,
dbus: (INTERFACE, "RSSIHighThreshold", i16, OPTIONAL),
get: (rssi_high_threshold, v => {v.to_owned()}),
);

property(
RSSILowTimeout, i16,
dbus: (INTERFACE, "RSSILowTimeout", i16, OPTIONAL),
get: (rssi_low_timeout, v => {v.to_owned()}),
);

property(
RSSIHighTimeout, i16,
dbus: (INTERFACE, "RSSIHighTimeout", i16, OPTIONAL),
get: (rssi_high_timeout, v => {v.to_owned()}),
);

property(
RSSISamplingPeriod, u16,
dbus: (INTERFACE, "RSSISamplingPeriod", u16, OPTIONAL),
get: (rssi_sampling_period, v => {v.to_owned()}),
);

property(
Patterns, Vec<u8>,
dbus: (INTERFACE, "Patterns", Vec<u8>, OPTIONAL),
get: (patterns, v => { v.to_owned() }),
);
}
);

/// Handle to registered monitor.
///
/// Drop to unregister monitor.
pub struct MonitorHandle {
name: dbus::Path<'static>,
_drop_tx: oneshot::Sender<()>,
}

impl Drop for MonitorHandle {
fn drop(&mut self) {
// required for drop order
}
}

impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MonitorHandle {{ {} }}", &self.name)
}
}
4 changes: 4 additions & 0 deletions bluer/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::{
adapter,
adv::Advertisement,
agent::{Agent, AgentHandle, RegisteredAgent},
monitor::{Monitor, MonitorHandle, RegisteredMonitor},
all_dbus_objects, gatt, parent_path, Adapter, Error, ErrorKind, InternalErrorKind, Result, SERVICE_NAME,
};

Expand All @@ -53,6 +54,7 @@ pub(crate) struct SessionInner {
pub gatt_reg_characteristic_descriptor_token: IfaceToken<Arc<gatt::local::RegisteredDescriptor>>,
pub gatt_profile_token: IfaceToken<gatt::local::Profile>,
pub agent_token: IfaceToken<Arc<RegisteredAgent>>,
pub monitor_token: IfaceToken<Arc<RegisteredMonitor>>,
#[cfg(feature = "rfcomm")]
pub profile_token: IfaceToken<Arc<RegisteredProfile>>,
pub single_sessions: Mutex<HashMap<dbus::Path<'static>, SingleSessionTerm>>,
Expand Down Expand Up @@ -175,6 +177,7 @@ impl Session {
gatt::local::RegisteredDescriptor::register_interface(&mut crossroads);
let gatt_profile_token = gatt::local::Profile::register_interface(&mut crossroads);
let agent_token = RegisteredAgent::register_interface(&mut crossroads);
let monitor_token = RegisteredMonitor::register_interface(&mut crossroads);
#[cfg(feature = "rfcomm")]
let profile_token = RegisteredProfile::register_interface(&mut crossroads);

Expand All @@ -190,6 +193,7 @@ impl Session {
gatt_reg_characteristic_descriptor_token,
gatt_profile_token,
agent_token,
monitor_token,
#[cfg(feature = "rfcomm")]
profile_token,
single_sessions: Mutex::new(HashMap::new()),
Expand Down

0 comments on commit 71c8ca7

Please sign in to comment.