use core::iter::once;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::sync::Arc;
use std::collections::HashMap;
use std::os::fd::{FromRawFd, RawFd};
use std::os::unix::net::UnixDatagram;
use async_channel::{Receiver, Sender};
use async_io::Async;
use embassy_futures::select::{select, select3, Either};
use embassy_time::{Duration, Instant, Timer};
use futures_lite::StreamExt;
use uuid::Uuid;
use zbus::fdo::{ObjectManager, ObjectManagerProxy};
use zbus::names::OwnedInterfaceName;
use zbus::object_server::Interface;
use zbus::zvariant::{ObjectPath, OwnedFd, OwnedObjectPath, OwnedValue, Value};
use zbus::{interface, Connection};
use crate::error::{Error, ErrorCode};
use crate::transport::network::btp::Btp;
use crate::transport::network::mdns::CommissionableFilter;
use crate::transport::network::BtAddr;
use crate::utils::select::Coalesce;
use crate::utils::zbus_proxies::bluez::adapter::AdapterProxy;
use crate::utils::zbus_proxies::bluez::device::DeviceProxy;
use crate::utils::zbus_proxies::bluez::gatt_characteristic::GattCharacteristicProxy;
use crate::utils::zbus_proxies::bluez::gatt_manager::GattManagerProxy;
use crate::utils::zbus_proxies::bluez::le_advertising_manager::LEAdvertisingManagerProxy;
use super::{AdvData, C1_CHARACTERISTIC_UUID, C2_CHARACTERISTIC_UUID, MATTER_BLE_SERVICE_UUID};
const BLUEZ_MATTER_BLE_SERVICE_UUID: Uuid = Uuid::from_u128(MATTER_BLE_SERVICE_UUID);
const BLUEZ_MATTER_C1_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(C1_CHARACTERISTIC_UUID);
const BLUEZ_MATTER_C2_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(C2_CHARACTERISTIC_UUID);
const BLUEZ_PATH_PREFIX: &str = "/org/projectchip/rs_matter/bluez";
pub const DEFAULT_SCAN_TIMEOUT_SECS: u16 = 60;
const SCAN_POLL_INTERVAL_MS: u64 = 1000;
const CONNECT_ATTEMPTS: u8 = 4;
const CONNECT_RETRY_DELAY_MS: u64 = 500;
async fn device_proxy<'a>(
connection: &'a Connection,
path: &OwnedObjectPath,
) -> Result<DeviceProxy<'a>, Error> {
Ok(DeviceProxy::builder(connection)
.destination("org.bluez")?
.path(path.clone())?
.build()
.await?)
}
fn iface_uuid(
interfaces: &HashMap<OwnedInterfaceName, HashMap<String, OwnedValue>>,
iface: &str,
) -> Option<String> {
let uuid = interfaces.get(iface)?.get("UUID")?;
String::try_from(uuid.clone()).ok()
}
async fn gatt_characteristic_proxy<'a>(
connection: &'a Connection,
path: &OwnedObjectPath,
) -> Result<GattCharacteristicProxy<'a>, Error> {
Ok(GattCharacteristicProxy::builder(connection)
.destination("org.bluez")?
.path(path.clone())?
.build()
.await?)
}
pub async fn run_peripheral(
connection: &Connection,
adapter_name: Option<&str>,
service_name: &str,
service_adv_data: &AdvData,
btp: &Btp,
) -> Result<(), Error> {
let adapter_path = adapter_path(connection, adapter_name).await?;
let adapter = AdapterProxy::new(connection, adapter_path.as_ref()).await?;
adapter.set_powered(true).await?;
let (write_sender, write_receiver) = async_channel::bounded(1);
let (notify_sender, notify_receiver) = async_channel::bounded(1);
let notifier_created = Arc::new(AtomicBool::new(false));
let mut app = AppReg::new(
connection,
service_name,
service_adv_data,
adapter_path.as_ref(),
write_sender,
notify_sender,
notifier_created.clone(),
)
.await?;
info!(
"Serving Matter GATT BTP service on Bluetooth adapter {}",
adapter_path
);
loop {
info!(
"Advertising Matter GATT BTP service on Bluetooth adapter {}",
adapter_path,
);
app.start_adv().await?;
let notifier = notify_receiver.recv().await.unwrap();
app.stop_adv().await?;
btp.reset();
select3(
wait_complete(btp, ¬ifier),
process_write(btp, &write_receiver),
process_indicate(btp, None, ¬ifier, &mut [0; 512]),
)
.coalesce()
.await?;
notifier_created.store(false, Ordering::SeqCst);
}
}
pub async fn run_central(
connection: &Connection,
adapter_name: Option<&str>,
addr: BtAddr,
btp: &Btp,
) -> Result<(), Error> {
let adapter_path = adapter_path_for_central(connection, adapter_name).await?;
let adapter = AdapterProxy::new(connection, adapter_path.as_ref()).await?;
adapter.set_powered(true).await?;
let device_path = device_path_for(&adapter_path, addr)?;
info!(
"Connecting to commissionable device {} ({})",
device_path, addr
);
let device = device_proxy(connection, &device_path).await?;
connect_with_retry(&device).await?;
wait_services_resolved(&device).await?;
let (c1, c2) = discover_matter_characteristics(connection, &device_path).await?;
let gatt_mtu = c1.mtu().await.ok().filter(|mtu| *mtu > 0);
debug!(
"Discovered Matter GATT characteristics C1/C2, ATT MTU: {:?}",
gatt_mtu
);
c2.start_notify().await?;
let mut value_changed = c2.receive_value_changed().await;
btp.set_initiator(true);
let result = select3(
wait_central_complete(btp, &device_path, connection),
process_c2_indications(btp, addr, gatt_mtu, &mut value_changed),
process_c1_writes(btp, gatt_mtu, &c1, &mut [0; 512]),
)
.coalesce()
.await;
let _ = c2.stop_notify().await;
result
}
pub async fn scan<F, R>(
connection: &Connection,
adapter_name: Option<&str>,
filter: &CommissionableFilter,
scan_timeout: Option<u16>,
mut on_found: F,
) -> Result<R, Error>
where
F: FnMut(BtAddr, &AdvData) -> Option<R>,
{
let adapter_path = adapter_path_for_central(connection, adapter_name).await?;
let adapter = AdapterProxy::new(connection, adapter_path.as_ref()).await?;
adapter.set_powered(true).await?;
info!(
"Scanning for a commissionable Matter device on Bluetooth adapter {} (filter: {:?})",
adapter_path, filter
);
let transport = Value::from("le");
let mut discovery_filter: HashMap<&str, &Value<'_>> = HashMap::new();
discovery_filter.insert("Transport", &transport);
adapter.set_discovery_filter(discovery_filter).await?;
adapter.start_discovery().await?;
let om = ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
let deadline = Instant::now()
+ Duration::from_secs(scan_timeout.unwrap_or(DEFAULT_SCAN_TIMEOUT_SECS) as u64);
let mut reported: heapless::Vec<BtAddr, 16> = heapless::Vec::new();
let outcome: Result<Option<R>, Error> = async {
loop {
if let Some(result) =
report_matching_devices(&om, &adapter_path, filter, &mut reported, &mut on_found)
.await?
{
return Ok(Some(result));
}
if Instant::now() >= deadline {
return Ok(None);
}
Timer::after(Duration::from_millis(SCAN_POLL_INTERVAL_MS)).await;
}
}
.await;
let _ = adapter.stop_discovery().await;
let outcome = outcome?;
outcome.ok_or_else(|| {
warn!(
"No commissionable Matter device matching the filter was found within the scan timeout"
);
ErrorCode::NoNetworkInterface.into()
})
}
async fn process_c2_indications(
btp: &Btp,
peer_addr: BtAddr,
gatt_mtu: Option<u16>,
value_changed: &mut zbus::proxy::PropertyStream<'_, Vec<u8>>,
) -> Result<(), Error> {
while let Some(change) = value_changed.next().await {
let value = change.get().await?;
if value.is_empty() {
continue;
}
trace!(
"Received C2 indication from peer {}: {:?}",
peer_addr,
value
);
btp.process_incoming(gatt_mtu, peer_addr, &value)?;
}
Ok(())
}
async fn process_c1_writes(
btp: &Btp,
gatt_mtu: Option<u16>,
c1: &GattCharacteristicProxy<'_>,
buf: &mut [u8],
) -> Result<(), Error> {
let mut options = HashMap::new();
let write_type = Value::from("request");
options.insert("type", &write_type);
loop {
let len = btp.process_outgoing(gatt_mtu, buf)?;
if len > 0 {
trace!("Writing to C1: {:?}", &buf[..len]);
c1.write_value(&buf[..len], options.clone()).await?;
} else {
btp.wait_outgoing().await;
}
}
}
async fn wait_central_complete(
btp: &Btp,
_device_path: &OwnedObjectPath,
_connection: &Connection,
) -> Result<(), Error> {
btp.wait_timeout().await;
info!("Timeout while waiting for data from the peer");
Ok(())
}
async fn report_matching_devices<F, R>(
om: &ObjectManagerProxy<'_>,
adapter_path: &OwnedObjectPath,
filter: &CommissionableFilter,
reported: &mut heapless::Vec<BtAddr, 16>,
on_found: &mut F,
) -> Result<Option<R>, Error>
where
F: FnMut(BtAddr, &AdvData) -> Option<R>,
{
let matter_uuid = BLUEZ_MATTER_BLE_SERVICE_UUID.to_string();
let objects = om.get_managed_objects().await?;
for (path, interfaces) in objects {
let Some(device) = interfaces.get("org.bluez.Device1") else {
continue;
};
if !path.as_str().starts_with(adapter_path.as_str()) {
continue;
}
let Some(service_data) = device.get("ServiceData") else {
continue;
};
let Ok(service_data) = <HashMap<String, OwnedValue>>::try_from(service_data.clone()) else {
continue;
};
let Some(matter_data) = service_data
.iter()
.find(|(uuid, _)| uuid.eq_ignore_ascii_case(&matter_uuid))
.map(|(_, data)| data)
else {
continue;
};
let Ok(bytes) = <Vec<u8>>::try_from(matter_data.clone()) else {
continue;
};
let Some(adv) = AdvData::parse_service_data(&bytes) else {
continue;
};
if !adv.matches(filter) {
continue;
}
let Ok(addr) = bt_addr_from_device_path(&path.as_ref()) else {
continue;
};
if reported.contains(&addr) {
continue;
}
let _ = reported.push(addr);
debug!(
"Matched commissionable device {} ({}) (adv: {:?})",
path, addr, adv
);
if let Some(result) = on_found(addr, &adv) {
return Ok(Some(result));
}
}
Ok(None)
}
fn device_path_for(adapter_path: &OwnedObjectPath, addr: BtAddr) -> Result<OwnedObjectPath, Error> {
let [a, b, c, d, e, f] = addr.0;
let path = format!(
"{}/dev_{:02X}_{:02X}_{:02X}_{:02X}_{:02X}_{:02X}",
adapter_path.as_str(),
a,
b,
c,
d,
e,
f
);
Ok(path.try_into()?)
}
async fn connect_with_retry(device: &DeviceProxy<'_>) -> Result<(), Error> {
for attempt in 1..=CONNECT_ATTEMPTS {
match device.connect().await {
Ok(()) => return Ok(()),
Err(e) if attempt < CONNECT_ATTEMPTS => {
warn!(
"Connect attempt {}/{} failed ({:?}); retrying",
attempt, CONNECT_ATTEMPTS, e
);
Timer::after(Duration::from_millis(CONNECT_RETRY_DELAY_MS)).await;
}
Err(e) => return Err(e.into()),
}
}
Err(ErrorCode::NoNetworkInterface.into())
}
async fn wait_services_resolved(device: &DeviceProxy<'_>) -> Result<(), Error> {
if device.services_resolved().await.unwrap_or(false) {
return Ok(());
}
let mut resolved_changed = device.receive_services_resolved_changed().await;
while let Some(change) = resolved_changed.next().await {
if change.get().await.unwrap_or(false) {
break;
}
}
Ok(())
}
async fn discover_matter_characteristics<'a>(
connection: &'a Connection,
device_path: &OwnedObjectPath,
) -> Result<(GattCharacteristicProxy<'a>, GattCharacteristicProxy<'a>), Error> {
let om = ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
let objects = om.get_managed_objects().await?;
let matter_service_uuid = BLUEZ_MATTER_BLE_SERVICE_UUID.to_string();
let mut service_path: Option<OwnedObjectPath> = None;
for (path, interfaces) in &objects {
if !path.as_str().starts_with(device_path.as_str()) {
continue;
}
let Some(uuid) = iface_uuid(interfaces, "org.bluez.GattService1") else {
continue;
};
if uuid.eq_ignore_ascii_case(&matter_service_uuid) {
service_path = Some(path.clone());
break;
}
}
let service_path = service_path.ok_or_else(|| {
warn!("The connected device does not expose the Matter GATT service");
Error::from(ErrorCode::NoNetworkInterface)
})?;
let mut c1: Option<GattCharacteristicProxy<'a>> = None;
let mut c2: Option<GattCharacteristicProxy<'a>> = None;
let c1_uuid = BLUEZ_MATTER_C1_CHARACTERISTIC_UUID.to_string();
let c2_uuid = BLUEZ_MATTER_C2_CHARACTERISTIC_UUID.to_string();
for (path, interfaces) in &objects {
if !path.as_str().starts_with(service_path.as_str()) {
continue;
}
let Some(uuid) = iface_uuid(interfaces, "org.bluez.GattCharacteristic1") else {
continue;
};
if uuid.eq_ignore_ascii_case(&c1_uuid) {
c1 = Some(gatt_characteristic_proxy(connection, path).await?);
} else if uuid.eq_ignore_ascii_case(&c2_uuid) {
c2 = Some(gatt_characteristic_proxy(connection, path).await?);
}
}
let c1 = c1.ok_or_else(|| {
warn!("The Matter GATT service is missing the C1 characteristic");
Error::from(ErrorCode::NoNetworkInterface)
})?;
let c2 = c2.ok_or_else(|| {
warn!("The Matter GATT service is missing the C2 characteristic");
Error::from(ErrorCode::NoNetworkInterface)
})?;
Ok((c1, c2))
}
fn bt_addr_from_device_path(path: &ObjectPath<'_>) -> Result<BtAddr, Error> {
let bt_addr_str = path
.as_str()
.rsplit('/')
.next()
.and_then(|last| last.strip_prefix("dev_"))
.ok_or(ErrorCode::InvalidData)?;
let bt_addr = bt_addr_str
.split('_')
.map(|s| u8::from_str_radix(s, 16).map_err(|_| Error::from(ErrorCode::InvalidData)))
.collect::<Result<heapless::Vec<_, 6>, _>>()?;
bt_addr
.into_array()
.map(BtAddr)
.map_err(|_| ErrorCode::InvalidData.into())
}
async fn adapter_path_for_central(
connection: &Connection,
adapter_name: Option<&str>,
) -> Result<OwnedObjectPath, Error> {
let om = ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
let objects = om.get_managed_objects().await?;
objects
.into_iter()
.find(|(path, interfaces)| {
if interfaces.contains_key("org.bluez.Adapter1") {
adapter_name
.map(|adapter_name| path.as_str().split('/').next_back() == Some(adapter_name))
.unwrap_or(true)
} else {
false
}
})
.map(|(path, _)| path)
.ok_or_else(|| ErrorCode::NoNetworkInterface.into())
}
async fn process_write(
btp: &Btp,
receiver: &Receiver<(u16, BtAddr, Vec<u8>)>,
) -> Result<(), Error> {
while let Ok((mtu, addr, value)) = receiver.recv().await {
btp.process_incoming(Some(mtu), addr, &value)?;
}
Ok(())
}
async fn process_indicate(
btp: &Btp,
gatt_mtu: Option<u16>,
notifier: &Async<UnixDatagram>,
buf: &mut [u8],
) -> Result<(), Error> {
loop {
let len = btp.process_outgoing(gatt_mtu, buf)?;
if len > 0 {
notifier.send(&buf[..len]).await?;
trace!("Sent indication to peer: {:?}", &buf[..len]);
} else {
btp.wait_outgoing().await;
}
}
}
async fn wait_complete(btp: &Btp, notifier: &Async<UnixDatagram>) -> Result<(), Error> {
let mut confirmation = [0; 1];
loop {
let result = select(
notifier.read_with(|socket| socket.recv(&mut confirmation)),
btp.wait_timeout(),
)
.await;
match result {
Either::First(Ok(0)) => {
info!("Peer unsubscribed");
break;
}
Either::First(Ok(_)) => {
if confirmation[0] != 1 {
warn!(
"Unexpected indication confirmation from peer: {}",
confirmation[0]
);
}
}
Either::First(Err(err)) => return Err(err.into()),
Either::Second(_) => {
info!("Timeout while waiting for data from the peer");
break;
}
}
}
Ok(())
}
async fn adapter_path(
connection: &Connection,
adapter_name: Option<&str>,
) -> Result<OwnedObjectPath, Error> {
let om = ObjectManagerProxy::new(connection, "org.bluez", "/").await?;
let objects = om.get_managed_objects().await?;
let adapter_path = objects
.into_iter()
.find(|(path, interfaces)| {
if interfaces.contains_key("org.bluez.GattManager1")
&& interfaces.contains_key("org.bluez.Adapter1")
&& interfaces.contains_key("org.bluez.LEAdvertisingManager1")
{
adapter_name
.map(|adapter_name| path.as_str().split('/').next_back() == Some(adapter_name))
.unwrap_or(true)
} else {
false
}
})
.map(|(path, _)| path);
adapter_path.ok_or_else(|| ErrorCode::NoNetworkInterface.into())
}
fn create_socket(
_peer_addr: BtAddr,
) -> zbus::fdo::Result<(Async<UnixDatagram>, std::os::fd::OwnedFd)> {
let (local, remote) = uds_pair()
.map_err(|e| zbus::fdo::Error::Failed(format!("Failed to create UDS pair: {}", e)))?;
Ok((
local,
remote
.into_inner()
.map_err(|e| {
zbus::fdo::Error::Failed(format!("Failed to convert UDS to OwnedFd: {}", e))
})?
.into(),
))
}
fn uds_pair() -> std::io::Result<(Async<UnixDatagram>, Async<UnixDatagram>)> {
let mut sv: [RawFd; 2] = [0; 2];
if unsafe {
libc::socketpair(
libc::AF_LOCAL,
libc::SOCK_SEQPACKET | libc::SOCK_CLOEXEC,
0,
sv.as_mut_ptr(),
)
} == -1
{
return Err(std::io::Error::last_os_error());
}
let [fd1, fd2] = sv;
let local = Async::new(unsafe { UnixDatagram::from_raw_fd(fd1) })?;
let remote = Async::new(unsafe { UnixDatagram::from_raw_fd(fd2) })?;
Ok((local, remote))
}
struct AdObj {
name: String,
service_data: Vec<u8>,
}
impl AdObj {
fn new(name: &str, data: &AdvData) -> Self {
Self {
name: name.to_string(),
service_data: data.service_payload_iter().collect(),
}
}
}
#[interface(name = "org.bluez.LEAdvertisement1")]
impl AdObj {
#[zbus(property)]
pub fn local_name(&self) -> &str {
&self.name
}
#[zbus(property, name = "Type")]
pub fn adv_type(&self) -> &str {
"peripheral"
}
#[zbus(property)]
pub fn discoverable(&self) -> bool {
true
}
#[zbus(property, name = "ServiceUUIDs")]
pub fn service_uuids(&self) -> Vec<String> {
vec![BLUEZ_MATTER_BLE_SERVICE_UUID.to_string()]
}
#[zbus(property)]
pub fn service_data(&self) -> HashMap<String, OwnedValue> {
once((
BLUEZ_MATTER_BLE_SERVICE_UUID.to_string(),
unwrap!(Value::Array(self.service_data.as_slice().into()).try_to_owned()),
))
.collect()
}
}
struct ServiceObj;
impl ServiceObj {
fn dict_mtu(dict: &HashMap<&str, Value<'_>>) -> zbus::fdo::Result<u16> {
let mtu = dict
.get("mtu")
.ok_or_else(|| zbus::fdo::Error::InvalidArgs("`mtu` not present in dict".into()))?;
mtu.try_into().map_err(|_| {
zbus::fdo::Error::InvalidArgs(format!("`mtu` is not a valid u16: {}", mtu))
})
}
fn dict_peer_addr(dict: &HashMap<&str, Value<'_>>) -> zbus::fdo::Result<BtAddr> {
let device = dict
.get("device")
.ok_or_else(|| zbus::fdo::Error::InvalidArgs("`device` not present in dict".into()))?;
Self::peer_addr(&device.try_into().map_err(|_| {
zbus::fdo::Error::InvalidArgs(format!("`device` is not a valid ObjectPath: {}", device))
})?)
}
fn peer_addr(path: &ObjectPath<'_>) -> zbus::fdo::Result<BtAddr> {
let err = || {
zbus::fdo::Error::InvalidArgs(format!("`device` path is not valid, expected `/<adapter-path>/dev_<bt_addr_hex1>_.._<bt_addr_hex6>`: {}", path))
};
let bt_addr_str = path
.as_str()
.rsplit('/')
.next()
.ok_or_else(err)?
.strip_prefix("dev_")
.ok_or_else(err)?;
let bt_addr = bt_addr_str
.split('_')
.map(|s| u8::from_str_radix(s, 16).map_err(|_| err()))
.collect::<Result<heapless::Vec<_, 6>, _>>()?;
bt_addr.into_array().map(BtAddr).map_err(|_| err())
}
}
#[interface(name = "org.bluez.GattService1")]
impl ServiceObj {
#[zbus(property, name = "UUID")]
fn uuid(&self) -> String {
BLUEZ_MATTER_BLE_SERVICE_UUID.to_string()
}
#[zbus(property)]
fn primary(&self) -> bool {
true
}
}
struct C1Obj {
service: OwnedObjectPath,
callback: Sender<(u16, BtAddr, Vec<u8>)>,
}
impl C1Obj {
fn new(service: OwnedObjectPath, callback: Sender<(u16, BtAddr, Vec<u8>)>) -> Self {
Self { service, callback }
}
}
#[interface(name = "org.bluez.GattCharacteristic1")]
impl C1Obj {
#[zbus(property, name = "UUID")]
fn uuid(&self) -> String {
BLUEZ_MATTER_C1_CHARACTERISTIC_UUID.to_string()
}
#[zbus(property)]
fn flags(&self) -> Vec<String> {
vec!["write".to_string()]
}
#[zbus(property)]
fn service(&self) -> OwnedObjectPath {
self.service.clone()
}
async fn write_value(
&self,
value: &[u8],
options: HashMap<&str, Value<'_>>,
) -> zbus::fdo::Result<()> {
let peer_addr = ServiceObj::dict_peer_addr(&options)?;
trace!(
"Received write request for C1 characteristic from peer {}: {:?}",
peer_addr,
value
);
self.callback
.send((ServiceObj::dict_mtu(&options)?, peer_addr, value.to_vec()))
.await
.unwrap();
Ok(())
}
}
struct C2Obj {
service: OwnedObjectPath,
callback: Sender<Async<UnixDatagram>>,
notifier_created: Arc<AtomicBool>,
}
impl C2Obj {
fn new(
service: OwnedObjectPath,
callback: Sender<Async<UnixDatagram>>,
notifier_created: Arc<AtomicBool>,
) -> Self {
Self {
service,
callback,
notifier_created,
}
}
}
#[interface(name = "org.bluez.GattCharacteristic1")]
impl C2Obj {
#[zbus(property, name = "UUID")]
fn uuid(&self) -> String {
BLUEZ_MATTER_C2_CHARACTERISTIC_UUID.to_string()
}
#[zbus(property)]
fn flags(&self) -> Vec<String> {
vec!["indicate".to_string()]
}
#[zbus(property)]
fn service(&self) -> OwnedObjectPath {
self.service.clone()
}
#[zbus(property)]
fn notify_acquired(&self) -> bool {
false }
async fn acquire_notify(
&self,
options: HashMap<&str, Value<'_>>,
) -> zbus::fdo::Result<(OwnedFd, u16)> {
let peer_addr = ServiceObj::dict_peer_addr(&options)?;
let mtu = ServiceObj::dict_mtu(&options)?;
trace!(
"Received acquire_notify request for C2 characteristic from peer {}",
peer_addr
);
if self.notifier_created.swap(true, Ordering::SeqCst) {
return Err(zbus::fdo::Error::Failed(
"Notifier already created for C2 characteristic".into(),
));
}
let (socket, fd) = create_socket(peer_addr)?;
self.callback.send(socket).await.unwrap();
Ok((fd.into(), mtu))
}
}
struct AppReg<'a> {
app_path: OwnedObjectPath,
app: ObjReg<'a, ObjectManager>,
gm: GattManagerProxy<'a>,
lm: LEAdvertisingManagerProxy<'a>,
service: ObjReg<'a, ServiceObj>,
c1: ObjReg<'a, C1Obj>,
c2: ObjReg<'a, C2Obj>,
ad: ObjReg<'a, AdObj>,
closed: bool,
}
impl<'a> AppReg<'a> {
async fn new(
conn: &'a Connection,
service_adv_name: &str,
service_adv_data: &AdvData,
adapter: ObjectPath<'a>,
c1_cb: Sender<(u16, BtAddr, Vec<u8>)>,
c2_cb: Sender<Async<UnixDatagram>>,
c2_notifier_created: Arc<AtomicBool>,
) -> Result<Self, Error> {
let app_id = Uuid::new_v4().simple().to_string();
let app_path = Self::path_for(&app_id, "app")?;
let app = ObjReg::new(conn, app_path.clone(), ObjectManager).await?;
let service =
ObjReg::new(conn, Self::path_for(&app_id, "app/service")?, ServiceObj).await?;
let c1 = ObjReg::new(
conn,
Self::path_for(&app_id, "app/service/c1")?,
C1Obj::new(service.path().into(), c1_cb),
)
.await?;
let c2 = ObjReg::new(
conn,
Self::path_for(&app_id, "app/service/c2")?,
C2Obj::new(service.path().into(), c2_cb, c2_notifier_created),
)
.await?;
let ad = ObjReg::new(
conn,
Self::path_for(&app_id, "ad")?,
AdObj::new(service_adv_name, service_adv_data),
)
.await?;
let gm = GattManagerProxy::new(conn, adapter.clone()).await?;
gm.register_application(&app_path.as_ref(), HashMap::new())
.await?;
let lm = LEAdvertisingManagerProxy::new(conn, adapter).await?;
Ok(Self {
app_path,
app,
gm,
lm,
service,
c1,
c2,
ad,
closed: false,
})
}
async fn start_adv(&mut self) -> Result<(), Error> {
if !self.closed {
self.lm
.register_advertisement(&self.ad.path(), HashMap::new())
.await?;
}
Ok(())
}
async fn stop_adv(&mut self) -> Result<(), Error> {
if !self.closed {
self.lm.unregister_advertisement(&self.ad.path()).await?;
}
Ok(())
}
async fn close(&mut self) -> Result<(), Error> {
if !self.closed {
self.stop_adv().await?;
self.ad.deregister().await?;
self.gm.unregister_application(&self.app_path).await?;
self.c2.deregister().await?;
self.c1.deregister().await?;
self.service.deregister().await?;
self.app.deregister().await?;
self.closed = true;
}
Ok(())
}
fn path_for(app_id: &str, obj_name: &str) -> Result<OwnedObjectPath, Error> {
Ok(format!("{BLUEZ_PATH_PREFIX}/{app_id}/{obj_name}").try_into()?)
}
}
impl Drop for AppReg<'_> {
fn drop(&mut self) {
futures_lite::future::block_on(self.close()).unwrap_or_else(|e| {
error!("Failed to deregister Matter presence: {}", e);
});
}
}
struct ObjReg<'a, T>
where
T: Interface,
{
connection: &'a Connection,
path: OwnedObjectPath,
registered: bool,
_t: PhantomData<fn() -> T>,
}
impl<'a, T> ObjReg<'a, T>
where
T: Interface,
{
async fn new(connection: &'a Connection, path: OwnedObjectPath, obj: T) -> Result<Self, Error> {
connection.object_server().at(&path, obj).await?;
Ok(Self {
connection,
path,
registered: true,
_t: PhantomData,
})
}
fn path(&self) -> ObjectPath<'_> {
self.path.as_ref()
}
async fn deregister(&mut self) -> Result<(), Error> {
if self.registered {
self.connection
.object_server()
.remove::<T, _>(&self.path)
.await?;
info!("Deregistered {}", self.path);
self.registered = false;
}
Ok(())
}
}
impl<T> Drop for ObjReg<'_, T>
where
T: Interface,
{
fn drop(&mut self) {
futures_lite::future::block_on(self.deregister()).unwrap_or_else(|e| {
error!("Failed to deregister {}: {}", self.path, e);
});
}
}