Skip to content

Commit 06d99b7

Browse files
committed
linux-rust: implement bluetooth logic with ui
finally! only copying all ui from android to first release
1 parent 5ea8d1d commit 06d99b7

8 files changed

Lines changed: 569 additions & 358 deletions

File tree

linux-rust/src/bluetooth/discovery.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ pub async fn find_other_managed_devices(adapter: &Adapter, managed_macs: Vec<Str
3737
if !devices.is_empty() {
3838
return Ok(devices);
3939
}
40+
debug!("No other managed devices found");
4041
Err(bluer::Error::from(Error::new(std::io::ErrorKind::NotFound, "No other managed devices found")))
4142
}
Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,39 @@
1-
use std::collections::HashMap;
21
use std::sync::Arc;
32
use crate::bluetooth::aacp::AACPManager;
43
use crate::bluetooth::att::ATTManager;
54

6-
pub enum BluetoothManager {
7-
AACP(Arc<AACPManager>),
8-
ATT(Arc<ATTManager>),
9-
}
10-
115
pub struct DeviceManagers {
126
att: Option<Arc<ATTManager>>,
137
aacp: Option<Arc<AACPManager>>,
148
}
159

1610
impl DeviceManagers {
17-
fn new() -> Self {
18-
Self { att: None, aacp: None }
19-
}
20-
21-
fn with_aacp(aacp: AACPManager) -> Self {
11+
pub fn with_aacp(aacp: AACPManager) -> Self {
2212
Self { att: None, aacp: Some(Arc::new(aacp)) }
2313
}
2414

25-
fn with_att(att: ATTManager) -> Self {
15+
pub fn with_att(att: ATTManager) -> Self {
2616
Self { att: Some(Arc::new(att)), aacp: None }
2717
}
28-
}
2918

30-
pub struct BluetoothDevices {
31-
devices: HashMap<String, DeviceManagers>,
32-
}
19+
// keeping the att for airpods optional as it requires changes in system bluez config
20+
pub fn with_both(aacp: AACPManager, att: ATTManager) -> Self {
21+
Self { att: Some(Arc::new(att)), aacp: Some(Arc::new(aacp)) }
22+
}
23+
24+
pub fn set_aacp(&mut self, manager: AACPManager) {
25+
self.aacp = Some(Arc::new(manager));
26+
}
3327

34-
impl BluetoothDevices {
35-
fn new() -> Self {
36-
Self { devices: HashMap::new() }
28+
pub fn set_att(&mut self, manager: ATTManager) {
29+
self.att = Some(Arc::new(manager));
3730
}
3831

39-
fn add_aacp(&mut self, mac: String, manager: AACPManager) {
40-
self.devices
41-
.entry(mac)
42-
.or_insert_with(DeviceManagers::new)
43-
.aacp = Some(Arc::new(manager));
32+
pub fn get_aacp(&self) -> Option<Arc<AACPManager>> {
33+
self.aacp.clone()
4434
}
4535

46-
fn add_att(&mut self, mac: String, manager: ATTManager) {
47-
self.devices
48-
.entry(mac)
49-
.or_insert_with(DeviceManagers::new)
50-
.att = Some(Arc::new(manager));
36+
pub fn get_att(&self) -> Option<Arc<ATTManager>> {
37+
self.att.clone()
5138
}
5239
}

linux-rust/src/devices/enums.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ impl Display for DeviceState {
5252

5353
#[derive(Clone, Debug)]
5454
pub struct AirPodsState {
55+
pub device_name: String,
5556
pub conversation_awareness_enabled: bool,
57+
pub personalized_volume_enabled: bool,
5658
}
5759

5860
#[derive(Clone, Debug)]

linux-rust/src/main.rs

Lines changed: 52 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ use std::collections::HashMap;
1414
use std::sync::Arc;
1515
use crate::bluetooth::discovery::{find_connected_airpods, find_other_managed_devices};
1616
use devices::airpods::AirPodsDevice;
17-
use bluer::Address;
17+
use bluer::{Address, InternalErrorKind};
1818
use ksni::TrayMethods;
1919
use crate::ui::tray::MyTray;
2020
use clap::Parser;
2121
use crate::bluetooth::le::start_le_monitor;
2222
use tokio::sync::mpsc::unbounded_channel;
23-
use crate::bluetooth::att::ATTHandles;
24-
use crate::bluetooth::managers::BluetoothManager;
23+
use tokio::sync::RwLock;
24+
use crate::bluetooth::managers::DeviceManagers;
2525
use crate::devices::enums::DeviceData;
26-
use crate::ui::messages::{AirPodsCommand, BluetoothUIMessage, NothingCommand, UICommand};
26+
use crate::ui::messages::BluetoothUIMessage;
2727
use crate::utils::get_devices_path;
2828

2929
#[derive(Parser)]
@@ -40,28 +40,29 @@ fn main() -> iced::Result {
4040
let args = Args::parse();
4141
let log_level = if args.debug { "debug" } else { "info" };
4242
if env::var("RUST_LOG").is_err() {
43-
unsafe { env::set_var("RUST_LOG", log_level.to_owned() + ",iced_wgpu=off,wgpu_hal=off,wgpu_core=off,librepods_rust::bluetooth::le=off,cosmic_text=off,naga=off,iced_winit=off") };
43+
unsafe { env::set_var("RUST_LOG", log_level.to_owned() + ",winit=warn,tracing=warn,,iced_wgpu=warn,wgpu_hal=warn,wgpu_core=warn,librepods_rust::bluetooth::le=warn,cosmic_text=warn,naga=warn,iced_winit=warn") };
4444
}
4545
env_logger::init();
4646

4747
let (ui_tx, ui_rx) = unbounded_channel::<BluetoothUIMessage>();
48-
let (ui_command_tx, ui_command_rx) = unbounded_channel::<UICommand>();
4948

49+
let device_managers: Arc<RwLock<HashMap<String, DeviceManagers>>> = Arc::new(RwLock::new(HashMap::new()));
50+
let device_managers_clone = device_managers.clone();
5051
std::thread::spawn(|| {
5152
let rt = tokio::runtime::Runtime::new().unwrap();
52-
rt.block_on(async_main(ui_tx, ui_command_rx)).unwrap();
53+
rt.block_on(async_main(ui_tx, device_managers_clone)).unwrap();
5354
});
5455

55-
ui::window::start_ui(ui_rx, args.start_minimized, ui_command_tx)
56+
ui::window::start_ui(ui_rx, args.start_minimized, device_managers)
5657
}
5758

5859

59-
async fn async_main(ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage>, mut ui_command_rx: tokio::sync::mpsc::UnboundedReceiver<UICommand>) -> bluer::Result<()> {
60+
async fn async_main(
61+
ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage>,
62+
device_managers: Arc<RwLock<HashMap<String, DeviceManagers>>>,
63+
) -> bluer::Result<()> {
6064
let args = Args::parse();
6165

62-
// let mut device_command_txs: HashMap<String, tokio::sync::mpsc::UnboundedSender<(ControlCommandIdentifiers, Vec<u8>)>> = HashMap::new();
63-
let mut device_managers: HashMap<String, Arc<BluetoothManager>> = HashMap::new();
64-
6566
let mut managed_devices_mac: Vec<String> = Vec::new(); // includes ony non-AirPods. AirPods handled separately.
6667

6768
let devices_path = get_devices_path();
@@ -125,12 +126,15 @@ async fn async_main(ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage
125126
let ui_tx_clone = ui_tx.clone();
126127
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(device.address().to_string())).unwrap();
127128
let airpods_device = AirPodsDevice::new(device.address(), tray_handle.clone(), ui_tx_clone).await;
128-
// device_command_txs.insert(device.address().to_string(), airpods_device.command_tx.unwrap());
129-
// device_managers.insert(device.address().to_string(), Arc::new(airpods_device.aacp_manager));
130-
device_managers.insert(
131-
device.address().to_string(),
132-
Arc::from(BluetoothManager::AACP(Arc::new(airpods_device.aacp_manager))),
133-
);
129+
130+
let mut managers = device_managers.write().await;
131+
let dev_managers = DeviceManagers::with_aacp(airpods_device.aacp_manager.clone());
132+
managers
133+
.entry(device.address().to_string())
134+
.or_insert(dev_managers)
135+
.set_aacp(airpods_device.aacp_manager)
136+
;
137+
drop(managers)
134138
}
135139
Err(_) => {
136140
info!("No connected AirPods found.");
@@ -144,30 +148,37 @@ async fn async_main(ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage
144148
info!("Found connected managed device: {}, initializing.", addr_str);
145149
let type_ = devices_list.get(&addr_str).unwrap().type_.clone();
146150
let ui_tx_clone = ui_tx.clone();
147-
let mut device_managers = device_managers.clone();
151+
let device_managers = device_managers.clone();
148152
tokio::spawn(async move {
149153
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap();
154+
let mut managers = device_managers.write().await;
150155
match type_ {
151156
devices::enums::DeviceType::Nothing => {
152157
let dev = devices::nothing::NothingDevice::new(device.address(), ui_tx_clone).await;
153-
device_managers.insert(
154-
addr_str,
155-
Arc::from(BluetoothManager::ATT(Arc::new(dev.att_manager))),
156-
);
158+
let dev_managers = DeviceManagers::with_att(dev.att_manager.clone());
159+
managers
160+
.entry(addr_str)
161+
.or_insert(dev_managers)
162+
.set_att(dev.att_manager);
157163
}
158164
_ => {}
159165
}
166+
drop(managers)
160167
});
161168
}
162169
}
163170
Err(e) => {
164-
log::error!("Error finding connected managed devices: {}", e);
171+
log::debug!("type of error: {:?}", e.kind);
172+
if e.kind != bluer::ErrorKind::Internal(InternalErrorKind::Io(std::io::ErrorKind::NotFound)) {
173+
log::error!("Error finding other managed devices: {}", e);
174+
} else {
175+
info!("No other managed devices found.");
176+
}
165177
}
166178
}
167179

168180
let conn = Connection::new_system()?;
169181
let rule = MatchRule::new_signal("org.freedesktop.DBus.Properties", "PropertiesChanged");
170-
let device_managers_clone = device_managers.clone();
171182
conn.add_match(rule, move |_: (), conn, msg| {
172183
let Some(path) = msg.path() else { return true; };
173184
if !path.contains("/org/bluez/hci") || !path.contains("/dev_") {
@@ -198,14 +209,17 @@ async fn async_main(ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage
198209
match type_ {
199210
devices::enums::DeviceType::Nothing => {
200211
let ui_tx_clone = ui_tx.clone();
201-
let mut device_managers = device_managers.clone();
212+
let device_managers = device_managers.clone();
202213
tokio::spawn(async move {
214+
let mut managers = device_managers.write().await;
203215
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap();
204216
let dev = devices::nothing::NothingDevice::new(addr, ui_tx_clone).await;
205-
device_managers.insert(
206-
addr_str,
207-
Arc::from(BluetoothManager::ATT(Arc::new(dev.att_manager))),
208-
);
217+
let dev_managers = DeviceManagers::with_att(dev.att_manager.clone());
218+
managers
219+
.entry(addr_str)
220+
.or_insert(dev_managers)
221+
.set_att(dev.att_manager);
222+
drop(managers);
209223
});
210224
}
211225
_ => {}
@@ -220,85 +234,20 @@ async fn async_main(ui_tx: tokio::sync::mpsc::UnboundedSender<BluetoothUIMessage
220234
info!("AirPods connected: {}, initializing", name);
221235
let handle_clone = tray_handle.clone();
222236
let ui_tx_clone = ui_tx.clone();
223-
let mut device_managers = device_managers.clone();
237+
let device_managers = device_managers.clone();
224238
tokio::spawn(async move {
225239
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap();
226240
let airpods_device = AirPodsDevice::new(addr, handle_clone, ui_tx_clone).await;
227-
device_managers.insert(
228-
addr_str,
229-
Arc::from(BluetoothManager::AACP(Arc::new(airpods_device.aacp_manager))),
230-
);
241+
let mut managers = device_managers.write().await;
242+
let dev_managers = DeviceManagers::with_aacp(airpods_device.aacp_manager.clone());
243+
managers
244+
.entry(addr_str)
245+
.or_insert(dev_managers)
246+
.set_aacp(airpods_device.aacp_manager);
247+
drop(managers);
231248
});
232249
true
233250
})?;
234-
tokio::spawn(async move {
235-
while let Some(command) = ui_command_rx.recv().await {
236-
match command {
237-
UICommand::AirPods(AirPodsCommand::SetControlCommandStatus(mac, identifier, value)) => {
238-
if let Some(manager) = device_managers_clone.get(&mac) {
239-
match manager.as_ref() {
240-
BluetoothManager::AACP(manager) => {
241-
log::debug!("Sending control command to device {}: {:?} = {:?}", mac, identifier, value);
242-
if let Err(e) = manager.send_control_command(identifier, value.as_ref()).await {
243-
log::error!("Failed to send control command to device {}: {}", mac, e);
244-
}
245-
}
246-
_ => {
247-
log::warn!("AACP not available for {}", mac);
248-
}
249-
}
250-
} else {
251-
log::warn!("No manager for device {}", mac);
252-
}
253-
}
254-
UICommand::AirPods(AirPodsCommand::RenameDevice(mac, new_name)) => {
255-
if let Some(manager) = device_managers_clone.get(&mac) {
256-
match manager.as_ref() {
257-
BluetoothManager::AACP(manager) => {
258-
log::debug!("Renaming device {} to {}", mac, new_name);
259-
if let Err(e) = manager.send_rename_packet(&new_name).await {
260-
log::error!("Failed to rename device {}: {}", mac, e);
261-
}
262-
}
263-
_ => {
264-
log::warn!("AACP not available for {}", mac);
265-
}
266-
}
267-
} else {
268-
log::warn!("No manager for device {}", mac);
269-
}
270-
}
271-
UICommand::Nothing(NothingCommand::SetNoiseCancellationMode(mac, mode)) => {
272-
if let Some(manager) = device_managers_clone.get(&mac) {
273-
match manager.as_ref() {
274-
BluetoothManager::ATT(manager) => {
275-
log::debug!("Setting noise cancellation mode for device {}: {:?}", mac, mode);
276-
if let Err(e) = manager.write(
277-
ATTHandles::NothingEverything,
278-
&[
279-
0x55,
280-
0x60, 0x01,
281-
0x0F, 0xF0,
282-
0x03, 0x00,
283-
0x00, 0x01, // the 0x00 is an incremental counter, but it works without it
284-
mode.to_byte(), 0x00,
285-
0x00, 0x00 // these both bytes were something random, 0 works too
286-
]
287-
).await {
288-
log::error!("Failed to set noise cancellation mode for device {}: {}", mac, e);
289-
}
290-
}
291-
_ => {
292-
log::warn!("Nothing manager not available for {}", mac);
293-
}
294-
}
295-
} else {
296-
log::warn!("No manager for device {}", mac);
297-
}
298-
}
299-
}
300-
}
301-
});
302251

303252
info!("Listening for Bluetooth connections via D-Bus...");
304253
loop {

0 commit comments

Comments
 (0)