Skip to content

Commit d613f7d

Browse files
committed
fix: use rfkill when toggling bluetooth
1 parent 5ee239d commit d613f7d

File tree

1 file changed

+52
-10
lines changed

1 file changed

+52
-10
lines changed

subscriptions/bluetooth/src/adapter.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
use crate::{Active, Event};
55
use std::{
66
collections::HashMap,
7+
convert::Infallible,
78
hash::{Hash, Hasher},
9+
path::PathBuf,
810
time::Duration,
911
};
1012
use zbus::zvariant::OwnedObjectPath;
@@ -217,24 +219,64 @@ pub async fn stop_discovery(connection: zbus::Connection, adapter_path: OwnedObj
217219
Event::Ok
218220
}
219221

222+
// In some distros, rfkill is only in sbin, which isn't normally in PATH
223+
// TODO: Directly access `/dev/rfkill`
224+
pub fn rfkill_path_var() -> std::ffi::OsString {
225+
let mut path = std::env::var_os("PATH").unwrap_or_default();
226+
path.push(":/usr/sbin");
227+
path
228+
}
229+
220230
pub async fn change_adapter_status(
221231
connection: zbus::Connection,
222232
adapter_path: OwnedObjectPath,
223233
active: bool,
224234
) -> Event {
235+
// rfkill will be persisted after reboot
236+
let adapter = bluez_zbus::get_adapter(&connection, adapter_path.clone())
237+
.await
238+
.map_err(|err| Ok::<_, Infallible>(Event::DBusError(err)))
239+
.unwrap();
240+
241+
let path = PathBuf::from(adapter_path.to_string());
242+
let name = path.file_name().unwrap();
243+
244+
let mut cmd = tokio::process::Command::new("rfkill");
245+
cmd.env("PATH", rfkill_path_var())
246+
.arg("--noheadings")
247+
.arg("--output")
248+
.arg("ID,DEVICE");
249+
250+
let rfkill_list = cmd.output().await.ok();
251+
252+
if let Some(id) = rfkill_list.and_then(|o| {
253+
let lines = String::from_utf8(o.stdout).ok()?;
254+
lines.split('\n').into_iter().find_map(|row| {
255+
let (id, cname) = row.trim().split_once(' ')?;
256+
(name == cname).then_some(id.to_string())
257+
})
258+
}) {
259+
if let Err(err) = tokio::process::Command::new("rfkill")
260+
.env("PATH", rfkill_path_var())
261+
.arg(if active { "unblock" } else { "block" })
262+
.arg(id)
263+
.output()
264+
.await
265+
{
266+
tracing::error!("Failed to set bluetooth state using rfkill. {err:?}");
267+
}
268+
if !active {
269+
return Event::Ok;
270+
}
271+
} else {
272+
tracing::error!("Failed to find rfkill ID for bluetooth adapter {name:?}");
273+
}
274+
225275
let mut result: zbus::Result<()> = Ok(());
226276
for attempt in 1..5 {
227277
result = async {
228-
let adapter = bluez_zbus::get_adapter(&connection, adapter_path.clone()).await?;
229-
if active {
230-
adapter.set_powered(true).await?;
231-
adapter.set_discoverable(true).await
232-
} else {
233-
if let Err(why) = adapter.set_discoverable(false).await {
234-
tracing::warn!("Unable to change discoverability: {why}");
235-
}
236-
adapter.set_powered(false).await
237-
}
278+
adapter.set_powered(true).await?;
279+
adapter.set_discoverable(true).await
238280
}
239281
.await;
240282
if let Err(why) = &result {

0 commit comments

Comments
 (0)