|
4 | 4 | use crate::{Active, Event}; |
5 | 5 | use std::{ |
6 | 6 | collections::HashMap, |
| 7 | + convert::Infallible, |
7 | 8 | hash::{Hash, Hasher}, |
| 9 | + path::PathBuf, |
8 | 10 | time::Duration, |
9 | 11 | }; |
10 | 12 | use zbus::zvariant::OwnedObjectPath; |
@@ -217,24 +219,64 @@ pub async fn stop_discovery(connection: zbus::Connection, adapter_path: OwnedObj |
217 | 219 | Event::Ok |
218 | 220 | } |
219 | 221 |
|
| 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 | + |
220 | 230 | pub async fn change_adapter_status( |
221 | 231 | connection: zbus::Connection, |
222 | 232 | adapter_path: OwnedObjectPath, |
223 | 233 | active: bool, |
224 | 234 | ) -> 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 | + |
225 | 275 | let mut result: zbus::Result<()> = Ok(()); |
226 | 276 | for attempt in 1..5 { |
227 | 277 | 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 |
238 | 280 | } |
239 | 281 | .await; |
240 | 282 | if let Err(why) = &result { |
|
0 commit comments