use log::{debug, warn};
use zbus::Connection;
use crate::Result;
use crate::api::models::{BluetoothDevice, ConnectionError, Device, DeviceIdentity, DeviceState};
use crate::core::bluetooth::populate_bluez_info;
use crate::core::state_wait::wait_for_wifi_device_ready;
use crate::dbus::{NMBluetoothProxy, NMDeviceProxy, NMProxy};
use crate::types::constants::device_type;
use crate::util::utils::get_ip_addresses_from_active_connection;
pub(crate) async fn list_devices(conn: &Connection) -> Result<Vec<Device>> {
let proxy = NMProxy::new(conn).await?;
let paths = proxy
.get_devices()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "failed to get device paths from NetworkManager".to_string(),
source: e,
})?;
let mut devices = Vec::new();
for p in paths {
let d_proxy = NMDeviceProxy::builder(conn)
.path(p.clone())?
.build()
.await?;
let interface = d_proxy
.interface()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("failed to get interface name for device {}", p.as_str()),
source: e,
})?;
let raw_type = d_proxy
.device_type()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("failed to get device type for {}", interface),
source: e,
})?;
let current_mac = match d_proxy.hw_address().await {
Ok(addr) => addr,
Err(e) => {
warn!(
"Failed to get hardware address for device {}: {}",
interface, e
);
String::from("00:00:00:00:00:00")
}
};
let perm_mac = match d_proxy.perm_hw_address().await {
Ok(addr) => addr,
Err(e) => {
debug!(
"Permanent hardware address not available for device {}: {}",
interface, e
);
current_mac.clone()
}
};
let device_type = raw_type.into();
let raw_state = d_proxy.state().await?;
let state = raw_state.into();
let managed = match d_proxy.managed().await {
Ok(m) => Some(m),
Err(e) => {
debug!(
"Failed to get 'managed' property for device {}: {}",
interface, e
);
None
}
};
let driver = match d_proxy.driver().await {
Ok(d) => Some(d),
Err(e) => {
debug!("Failed to get driver for device {}: {}", interface, e);
None
}
};
let (ip4_address, ip6_address) =
if let Ok(active_conn_path) = d_proxy.active_connection().await {
if active_conn_path.as_str() != "/" {
get_ip_addresses_from_active_connection(conn, &active_conn_path).await
} else {
(None, None)
}
} else {
(None, None)
};
devices.push(Device {
path: p.to_string(),
interface,
identity: DeviceIdentity::new(perm_mac, current_mac),
device_type,
state,
managed,
driver,
ip4_address,
ip6_address,
});
}
Ok(devices)
}
pub(crate) async fn is_connecting(conn: &Connection) -> Result<bool> {
let nm = NMProxy::new(conn).await?;
let devices = nm.get_devices().await?;
for dp in devices {
let dev = NMDeviceProxy::builder(conn)
.path(dp.clone())?
.build()
.await?;
let raw_state = dev
.state()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("failed to get state for device {}", dp.as_str()),
source: e,
})?;
let state: DeviceState = raw_state.into();
if state.is_transitional() {
return Ok(true);
}
}
Ok(false)
}
pub(crate) async fn list_bluetooth_devices(conn: &Connection) -> Result<Vec<BluetoothDevice>> {
let proxy = NMProxy::new(conn).await?;
let paths = proxy.get_devices().await?;
let mut devices = Vec::new();
for p in paths {
let d_proxy = NMDeviceProxy::builder(conn)
.path(p.clone())?
.build()
.await?;
let dev_type = d_proxy
.device_type()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!(
"failed to get device type for {} during Bluetooth scan",
p.as_str()
),
source: e,
})?;
if dev_type != device_type::BLUETOOTH {
continue;
}
let bd_proxy = NMBluetoothProxy::builder(conn)
.path(p.clone())?
.build()
.await?;
let bdaddr = bd_proxy
.hw_address()
.await
.unwrap_or_else(|_| String::from("00:00:00:00:00:00"));
let bt_caps = bd_proxy.bt_capabilities().await?;
let raw_state = d_proxy.state().await?;
let state = raw_state.into();
let bluez_info = populate_bluez_info(conn, &bdaddr).await?;
devices.push(BluetoothDevice::new(
bdaddr,
bluez_info.0,
bluez_info.1,
bt_caps,
state,
));
}
Ok(devices)
}
pub(crate) async fn wait_for_wifi_ready(conn: &Connection) -> Result<()> {
let nm = NMProxy::new(conn).await?;
let devices = nm.get_devices().await?;
for dev_path in devices {
let dev = NMDeviceProxy::builder(conn)
.path(dev_path.clone())?
.build()
.await?;
if dev.device_type().await? != device_type::WIFI {
continue;
}
debug!("Found Wi-Fi device, waiting for it to become ready");
let current_state = dev.state().await?;
let state = DeviceState::from(current_state);
if state == DeviceState::Disconnected || state == DeviceState::Activated {
debug!("Wi-Fi device already ready");
return Ok(());
}
return wait_for_wifi_device_ready(&dev).await;
}
Err(ConnectionError::NoWifiDevice)
}
pub(crate) async fn set_wifi_enabled(conn: &Connection, value: bool) -> Result<()> {
let nm = NMProxy::new(conn).await?;
Ok(nm.set_wireless_enabled(value).await?)
}
pub(crate) async fn wifi_enabled(conn: &Connection) -> Result<bool> {
let nm = NMProxy::new(conn).await?;
Ok(nm.wireless_enabled().await?)
}
pub(crate) async fn wifi_hardware_enabled(conn: &Connection) -> Result<bool> {
let nm = NMProxy::new(conn).await?;
Ok(nm.wireless_hardware_enabled().await?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::BluetoothNetworkRole;
#[test]
fn test_default_bluetooth_address() {
let default_addr = "00:00:00:00:00:00";
assert_eq!(default_addr.len(), 17);
assert_eq!(default_addr.matches(':').count(), 5);
}
#[test]
fn test_bluetooth_device_construction() {
let panu = BluetoothNetworkRole::PanU as u32;
let device = BluetoothDevice::new(
"00:1A:7D:DA:71:13".into(),
Some("TestDevice".into()),
Some("Test".into()),
panu,
DeviceState::Activated,
);
assert_eq!(device.bdaddr, "00:1A:7D:DA:71:13");
assert_eq!(device.name, Some("TestDevice".into()));
assert_eq!(device.alias, Some("Test".into()));
assert!(matches!(device.bt_caps, _panu));
assert_eq!(device.state, DeviceState::Activated);
}
}