1 use clap::{value_t, App, Arg};
2
3 use std::collections::{HashMap, HashSet};
4 use std::sync::{Arc, Mutex};
5 use std::time::Duration;
6
7 use dbus::channel::MatchingReceiver;
8 use dbus::message::MatchRule;
9 use dbus::nonblock::SyncConnection;
10 use dbus_crossroads::Crossroads;
11 use tokio::sync::mpsc;
12 use tokio::time::{sleep, timeout};
13
14 use crate::bt_adv::AdvSet;
15 use crate::bt_gatt::{GattClientContext, GattServerContext};
16 use crate::callbacks::{
17 AdminCallback, AdvertisingSetCallback, BatteryManagerCallback, BtCallback,
18 BtConnectionCallback, BtManagerCallback, BtSocketManagerCallback, MediaCallback, QACallback,
19 ScannerCallback, SuspendCallback, TelephonyCallback,
20 };
21 use crate::command_handler::{CommandHandler, SocketSchedule};
22 use crate::dbus_iface::{
23 BatteryManagerDBus, BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothLoggingDBus,
24 BluetoothManagerDBus, BluetoothMediaDBus, BluetoothQADBus, BluetoothQALegacyDBus,
25 BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus,
26 };
27 use crate::editor::AsyncEditor;
28 use bt_topshim::{btif::RawAddress, topstack};
29 use btstack::bluetooth::{BluetoothDevice, IBluetooth};
30 use btstack::suspend::ISuspend;
31 use manager_service::iface_bluetooth_manager::IBluetoothManager;
32
33 mod bt_adv;
34 mod bt_gatt;
35 mod callbacks;
36 mod command_handler;
37 mod console;
38 mod dbus_arg;
39 mod dbus_iface;
40 mod editor;
41
42 #[derive(Clone)]
43 pub(crate) struct GattRequest {
44 address: RawAddress,
45 id: i32,
46 offset: i32,
47 value: Vec<u8>,
48 }
49
50 /// Context structure for the client. Used to keep track details about the active adapter and its
51 /// state.
52 pub(crate) struct ClientContext {
53 /// List of adapters and whether they are enabled.
54 pub(crate) adapters: HashMap<i32, bool>,
55
56 // TODO(abps) - Change once we have multi-adapter support.
57 /// The default adapter is also the active adapter. Defaults to 0.
58 pub(crate) default_adapter: i32,
59
60 /// Current adapter is enabled?
61 pub(crate) enabled: bool,
62
63 /// Current adapter is ready to be used?
64 pub(crate) adapter_ready: bool,
65
66 /// Current adapter address if known.
67 pub(crate) adapter_address: Option<RawAddress>,
68
69 /// Currently active bonding attempt. If it is not none, we are currently attempting to bond
70 /// this device.
71 pub(crate) bonding_attempt: Option<BluetoothDevice>,
72
73 /// Is adapter discovering?
74 pub(crate) discovering_state: bool,
75
76 /// Devices found in current discovery session. List should be cleared when a new discovery
77 /// session starts so that previous results don't pollute current search.
78 pub(crate) found_devices: HashMap<String, BluetoothDevice>,
79
80 /// List of bonded devices.
81 pub(crate) bonded_devices: HashMap<String, BluetoothDevice>,
82
83 /// Proxy for manager interface.
84 pub(crate) manager_dbus: BluetoothManagerDBus,
85
86 /// Proxy for adapter interface. Only exists when the default adapter is enabled.
87 pub(crate) adapter_dbus: Option<BluetoothDBus>,
88
89 /// Proxy for adapter QA Legacy interface. Only exists when the default adapter is enabled.
90 pub(crate) qa_legacy_dbus: Option<BluetoothQALegacyDBus>,
91
92 /// Proxy for adapter QA interface.
93 pub(crate) qa_dbus: Option<BluetoothQADBus>,
94
95 /// Proxy for GATT interface.
96 pub(crate) gatt_dbus: Option<BluetoothGattDBus>,
97
98 /// Proxy for Admin interface.
99 pub(crate) admin_dbus: Option<BluetoothAdminDBus>,
100
101 /// Proxy for suspend interface.
102 pub(crate) suspend_dbus: Option<SuspendDBus>,
103
104 /// Proxy for socket manager interface.
105 pub(crate) socket_manager_dbus: Option<BluetoothSocketManagerDBus>,
106
107 /// Proxy for Telephony interface.
108 pub(crate) telephony_dbus: Option<BluetoothTelephonyDBus>,
109
110 /// Proxy for Media interface.
111 pub(crate) media_dbus: Option<BluetoothMediaDBus>,
112
113 /// Proxy for battery manager interface.
114 pub(crate) battery_manager_dbus: Option<BatteryManagerDBus>,
115
116 /// Proxy for logging interface.
117 pub(crate) logging_dbus: Option<BluetoothLoggingDBus>,
118
119 /// Channel to send actions to take in the foreground
120 fg: mpsc::Sender<ForegroundActions>,
121
122 /// Internal DBus connection object.
123 dbus_connection: Arc<SyncConnection>,
124
125 /// Internal DBus crossroads object.
126 dbus_crossroads: Arc<Mutex<Crossroads>>,
127
128 /// Identifies the callback to receive IScannerCallback method calls.
129 scanner_callback_id: Option<u32>,
130
131 /// Identifies the callback to receive IAdvertisingSetCallback method calls.
132 advertiser_callback_id: Option<u32>,
133
134 /// Identifies the callback to receive IBluetoothAdminPolicyCallback method calls.
135 admin_callback_id: Option<u32>,
136
137 /// Keeps track of active LE scanners.
138 active_scanner_ids: HashSet<u8>,
139
140 /// Keeps track of advertising sets registered. Map from reg_id to AdvSet.
141 adv_sets: HashMap<i32, AdvSet>,
142
143 /// Identifies the callback to receive IBluetoothSocketManagerCallback method calls.
144 socket_manager_callback_id: Option<u32>,
145
146 /// Identifies the callback to receive IBluetoothQACallback method calls.
147 qa_callback_id: Option<u32>,
148
149 /// Is btclient running in restricted mode?
150 is_restricted: bool,
151
152 /// Is btclient running in interactive mode?
153 is_interactive: bool,
154
155 /// Data of GATT client preference.
156 gatt_client_context: GattClientContext,
157
158 /// Data of GATT server preference.
159 gatt_server_context: GattServerContext,
160
161 /// The schedule when a socket is connected.
162 socket_test_schedule: Option<SocketSchedule>,
163
164 /// The handle of the SDP record for MPS (Multi-Profile Specification).
165 mps_sdp_handle: Option<i32>,
166
167 /// The set of client commands that need to wait for callbacks.
168 client_commands_with_callbacks: Vec<String>,
169
170 /// A set of addresses whose battery changes are being tracked.
171 pub(crate) battery_address_filter: HashSet<String>,
172
173 /// A request from a GATT client that is still being processed.
174 pending_gatt_request: Option<GattRequest>,
175 }
176
177 impl ClientContext {
new( dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, tx: mpsc::Sender<ForegroundActions>, is_restricted: bool, is_interactive: bool, client_commands_with_callbacks: Vec<String>, ) -> ClientContext178 pub fn new(
179 dbus_connection: Arc<SyncConnection>,
180 dbus_crossroads: Arc<Mutex<Crossroads>>,
181 tx: mpsc::Sender<ForegroundActions>,
182 is_restricted: bool,
183 is_interactive: bool,
184 client_commands_with_callbacks: Vec<String>,
185 ) -> ClientContext {
186 // Manager interface is almost always available but adapter interface
187 // requires that the specific adapter is enabled.
188 let manager_dbus = BluetoothManagerDBus::new(dbus_connection.clone());
189
190 ClientContext {
191 adapters: HashMap::new(),
192 default_adapter: 0,
193 enabled: false,
194 adapter_ready: false,
195 adapter_address: None,
196 bonding_attempt: None,
197 discovering_state: false,
198 found_devices: HashMap::new(),
199 bonded_devices: HashMap::new(),
200 manager_dbus,
201 adapter_dbus: None,
202 qa_legacy_dbus: None,
203 qa_dbus: None,
204 gatt_dbus: None,
205 admin_dbus: None,
206 suspend_dbus: None,
207 socket_manager_dbus: None,
208 telephony_dbus: None,
209 media_dbus: None,
210 battery_manager_dbus: None,
211 logging_dbus: None,
212 fg: tx,
213 dbus_connection,
214 dbus_crossroads,
215 scanner_callback_id: None,
216 advertiser_callback_id: None,
217 admin_callback_id: None,
218 active_scanner_ids: HashSet::new(),
219 adv_sets: HashMap::new(),
220 socket_manager_callback_id: None,
221 qa_callback_id: None,
222 is_restricted,
223 is_interactive,
224 gatt_client_context: GattClientContext::new(),
225 gatt_server_context: GattServerContext::new(),
226 socket_test_schedule: None,
227 mps_sdp_handle: None,
228 client_commands_with_callbacks,
229 battery_address_filter: HashSet::new(),
230 pending_gatt_request: None,
231 }
232 }
233
234 // Sets required values for the adapter when enabling or disabling
set_adapter_enabled(&mut self, hci_interface: i32, enabled: bool)235 fn set_adapter_enabled(&mut self, hci_interface: i32, enabled: bool) {
236 print_info!("hci{} enabled = {}", hci_interface, enabled);
237
238 self.adapters.entry(hci_interface).and_modify(|v| *v = enabled).or_insert(enabled);
239
240 // When the default adapter's state is updated, we need to modify a few more things.
241 // Only do this if we're not repeating the previous state.
242 let prev_enabled = self.enabled;
243 let default_adapter = self.default_adapter;
244 if hci_interface == default_adapter && prev_enabled != enabled {
245 self.enabled = enabled;
246 self.adapter_ready = false;
247 if enabled {
248 self.create_adapter_proxy(hci_interface);
249 } else {
250 self.adapter_dbus = None;
251 }
252 }
253 }
254
255 // Creates adapter proxy, registers callbacks and initializes address.
create_adapter_proxy(&mut self, idx: i32)256 fn create_adapter_proxy(&mut self, idx: i32) {
257 let conn = self.dbus_connection.clone();
258
259 let dbus = BluetoothDBus::new(conn.clone(), idx);
260 self.adapter_dbus = Some(dbus);
261 self.qa_legacy_dbus = Some(BluetoothQALegacyDBus::new(conn.clone(), idx));
262 self.qa_dbus = Some(BluetoothQADBus::new(conn.clone(), idx));
263
264 let gatt_dbus = BluetoothGattDBus::new(conn.clone(), idx);
265 self.gatt_dbus = Some(gatt_dbus);
266
267 let admin_dbus = BluetoothAdminDBus::new(conn.clone(), idx);
268 self.admin_dbus = Some(admin_dbus);
269
270 let socket_manager_dbus = BluetoothSocketManagerDBus::new(conn.clone(), idx);
271 self.socket_manager_dbus = Some(socket_manager_dbus);
272
273 self.suspend_dbus = Some(SuspendDBus::new(conn.clone(), idx));
274
275 self.telephony_dbus = Some(BluetoothTelephonyDBus::new(conn.clone(), idx));
276
277 self.media_dbus = Some(BluetoothMediaDBus::new(conn.clone(), idx));
278
279 self.battery_manager_dbus = Some(BatteryManagerDBus::new(conn.clone(), idx));
280
281 self.logging_dbus = Some(BluetoothLoggingDBus::new(conn.clone(), idx));
282
283 // Trigger callback registration in the foreground
284 let fg = self.fg.clone();
285 let is_interactive = self.is_interactive;
286 tokio::spawn(async move {
287 let adapter = format!("adapter{}", idx);
288
289 // Floss won't export the interface until it is ready to be used.
290 // Wait 1 second before registering the callbacks.
291 // Only introduce such delay on interactive mode. This is because we expect the user to
292 // ensure the adapter interface is ready when they issue the command in non-interactive
293 // mode. Otherwise, there will always have 1 second delay and in most of the case it is
294 // not needed.
295 if is_interactive {
296 sleep(Duration::from_millis(1000)).await;
297 }
298 let _ = fg.send(ForegroundActions::RegisterAdapterCallback(adapter)).await;
299 });
300 }
301
302 // Foreground-only: Updates the adapter address.
update_adapter_address(&mut self) -> RawAddress303 fn update_adapter_address(&mut self) -> RawAddress {
304 let address = self.adapter_dbus.as_ref().unwrap().get_address();
305 self.adapter_address = Some(address);
306
307 address
308 }
309
310 // Foreground-only: Updates bonded devices.
update_bonded_devices(&mut self)311 fn update_bonded_devices(&mut self) {
312 let bonded_devices = self.adapter_dbus.as_ref().unwrap().get_bonded_devices();
313
314 for device in bonded_devices {
315 self.bonded_devices.insert(device.address.to_string(), device.clone());
316 }
317 }
318
connect_all_enabled_profiles(&mut self, device: BluetoothDevice)319 fn connect_all_enabled_profiles(&mut self, device: BluetoothDevice) {
320 let fg = self.fg.clone();
321 tokio::spawn(async move {
322 let _ = fg.send(ForegroundActions::ConnectAllEnabledProfiles(device)).await;
323 });
324 }
325
run_callback(&mut self, callback: Box<dyn Fn(Arc<Mutex<ClientContext>>) + Send>)326 fn run_callback(&mut self, callback: Box<dyn Fn(Arc<Mutex<ClientContext>>) + Send>) {
327 let fg = self.fg.clone();
328 tokio::spawn(async move {
329 let _ = fg.send(ForegroundActions::RunCallback(callback)).await;
330 });
331 }
332
get_devices(&self) -> Vec<String>333 fn get_devices(&self) -> Vec<String> {
334 let mut result: Vec<String> = vec![];
335
336 result.extend(self.found_devices.keys().map(String::from).collect::<Vec<String>>());
337 result.extend(
338 self.bonded_devices
339 .keys()
340 .filter(|key| !self.found_devices.contains_key(&String::from(*key)))
341 .map(String::from)
342 .collect::<Vec<String>>(),
343 );
344
345 result
346 }
347
get_floss_api_version(&mut self) -> (u32, u32)348 fn get_floss_api_version(&mut self) -> (u32, u32) {
349 let ver = self.manager_dbus.get_floss_api_version();
350 let major = (ver & 0xFFFF_0000) >> 16;
351 let minor = ver & 0x0000_FFFF;
352 (major, minor)
353 }
354 }
355
356 /// Actions to take on the foreground loop. This allows us to queue actions in
357 /// callbacks that get run in the foreground context.
358 enum ForegroundActions {
359 ConnectAllEnabledProfiles(BluetoothDevice), // Connect all enabled profiles for this device
360 RunCallback(Box<dyn Fn(Arc<Mutex<ClientContext>>) + Send>), // Run callback in foreground
361 RegisterAdapterCallback(String), // Register callbacks for this adapter
362 Readline(rustyline::Result<String>), // Readline result from rustyline
363 }
364
365 /// Runs a command line program that interacts with a Bluetooth stack.
main() -> Result<(), Box<dyn std::error::Error>>366 fn main() -> Result<(), Box<dyn std::error::Error>> {
367 let matches = App::new("btclient")
368 .arg(Arg::with_name("restricted").long("restricted").takes_value(false))
369 .arg(
370 Arg::with_name("command")
371 .short("c")
372 .long("command")
373 .takes_value(true)
374 .help("Executes a non-interactive command"),
375 )
376 .arg(
377 Arg::with_name("timeout")
378 .short("t")
379 .long("timeout")
380 .takes_value(true)
381 .help("Specify a timeout in seconds for a non-interactive command"),
382 )
383 .get_matches();
384 let command = value_t!(matches, "command", String).ok();
385 let is_restricted = matches.is_present("restricted");
386 let is_interactive = command.is_none();
387 let timeout_secs = value_t!(matches, "timeout", u64);
388
389 topstack::get_runtime().block_on(async move {
390 // Connect to D-Bus system bus.
391 let (resource, conn) = dbus_tokio::connection::new_system_sync()?;
392
393 // The `resource` is a task that should be spawned onto a tokio compatible
394 // reactor ASAP. If the resource ever finishes, we lost connection to D-Bus.
395 tokio::spawn(async {
396 let err = resource.await;
397 panic!("Lost connection to D-Bus: {}", err);
398 });
399
400 // Sets up Crossroads for receiving callbacks.
401 let cr = Arc::new(Mutex::new(Crossroads::new()));
402 cr.lock().unwrap().set_async_support(Some((
403 conn.clone(),
404 Box::new(|x| {
405 tokio::spawn(x);
406 }),
407 )));
408 let cr_clone = cr.clone();
409 conn.start_receive(
410 MatchRule::new_method_call(),
411 Box::new(move |msg, conn| {
412 cr_clone.lock().unwrap().handle_message(msg, conn).unwrap();
413 true
414 }),
415 );
416
417 // Accept foreground actions with mpsc
418 let (tx, rx) = mpsc::channel::<ForegroundActions>(10);
419
420 // Include the commands
421 // (1) that will be run as non-interactive client commands, and
422 // (2) that will need to wait for the callbacks to complete.
423 let client_commands_with_callbacks = vec!["media".to_string()];
424
425 // Create the context needed for handling commands
426 let context = Arc::new(Mutex::new(ClientContext::new(
427 conn.clone(),
428 cr.clone(),
429 tx.clone(),
430 is_restricted,
431 is_interactive,
432 client_commands_with_callbacks,
433 )));
434
435 // Check if manager interface is valid. We only print some help text before failing on the
436 // first actual access to the interface (so we can also capture the actual reason the
437 // interface isn't valid).
438 if !context.lock().unwrap().manager_dbus.is_valid() {
439 println!("Bluetooth manager doesn't seem to be working correctly.");
440 println!("Check if service is running.");
441 return Ok(());
442 }
443
444 // TODO: Registering the callback should be done when btmanagerd is ready (detect with
445 // ObjectManager).
446 context.lock().unwrap().manager_dbus.register_callback(Box::new(BtManagerCallback::new(
447 String::from("/org/chromium/bluetooth/client/bluetooth_manager_callback"),
448 context.clone(),
449 conn.clone(),
450 cr.clone(),
451 )));
452
453 // Check if the default adapter is enabled. If yes, we should create the adapter proxy
454 // right away.
455 let default_adapter = context.lock().unwrap().default_adapter;
456
457 let default_adapter_enabled = {
458 let mut context_locked = context.lock().unwrap();
459 match context_locked.manager_dbus.rpc.get_adapter_enabled(default_adapter).await {
460 Ok(enabled) => {
461 if enabled {
462 context_locked.set_adapter_enabled(default_adapter, true);
463 }
464 enabled
465 }
466 Err(e) => {
467 panic!("Bluetooth Manager is not available. Exiting. D-Bus error: {}", e);
468 }
469 }
470 };
471
472 let handler = CommandHandler::new(context.clone());
473 if !is_interactive {
474 // Timeout applies only to non-interactive commands.
475 if let Ok(timeout_secs) = timeout_secs {
476 let timeout_duration = Duration::from_secs(timeout_secs);
477 match timeout(
478 timeout_duration,
479 handle_client_command(
480 handler,
481 tx,
482 rx,
483 context,
484 command,
485 default_adapter_enabled,
486 ),
487 )
488 .await
489 {
490 Ok(_) => {
491 return Result::Ok(());
492 }
493 Err(_) => {
494 return Result::Err("btclient timeout".into());
495 }
496 };
497 }
498 }
499 // There are two scenarios in which handle_client_command is run without a timeout.
500 // - Interactive commands: none of these commands require a timeout.
501 // - Non-interactive commands that have not specified a timeout.
502 handle_client_command(handler, tx, rx, context, command, default_adapter_enabled).await?;
503 Result::Ok(())
504 })
505 }
506
507 // If btclient runs without command arguments, the interactive shell actions are performed.
508 // If btclient runs with command arguments, the command is executed once.
509 // There are 2 cases to run the command and 2 cases to exit.
510 // Run:
511 // Case 1: If |run_command_on_ready|, run the command after the callbacks are registered
512 // successfully.
513 // Case 2: If not |run_command_on_ready|, run the command immediately.
514 // Exit:
515 // Case 1: if the command does not need a callback, e.g., "help", it will exit after running
516 // handler.process_cmd_line().
517 // Case 2: if the command needs a callback, e.g., "media log", it will exit after the callback
518 // has been run in the arm of ForegroundActions::RunCallback(callback).
handle_client_command( mut handler: CommandHandler, tx: mpsc::Sender<ForegroundActions>, mut rx: mpsc::Receiver<ForegroundActions>, context: Arc<Mutex<ClientContext>>, command: Option<String>, run_command_on_ready: bool, ) -> Result<(), Box<dyn std::error::Error>>519 async fn handle_client_command(
520 mut handler: CommandHandler,
521 tx: mpsc::Sender<ForegroundActions>,
522 mut rx: mpsc::Receiver<ForegroundActions>,
523 context: Arc<Mutex<ClientContext>>,
524 command: Option<String>,
525 run_command_on_ready: bool,
526 ) -> Result<(), Box<dyn std::error::Error>> {
527 if !run_command_on_ready {
528 if let Some(command) = command.as_ref() {
529 let mut iter = command.split(' ').map(String::from);
530 let first = iter.next().unwrap_or(String::from(""));
531 // Return immediately if the command fails to execute.
532 if !handler.process_cmd_line(&first, &iter.collect::<Vec<String>>()) {
533 return Err("failed process command".into());
534 }
535 // If there is no callback to wait for, we're done.
536 if !context.lock().unwrap().client_commands_with_callbacks.contains(&first) {
537 return Ok(());
538 }
539 }
540 }
541
542 let semaphore_fg = Arc::new(tokio::sync::Semaphore::new(1));
543
544 // If there are no command arguments, start the interactive shell.
545 if command.is_none() {
546 let command_rule_list = handler.get_command_rule_list().clone();
547 let context_for_closure = context.clone();
548
549 // Async task to keep reading new lines from user
550 let semaphore = semaphore_fg.clone();
551 let editor = AsyncEditor::new(command_rule_list, context_for_closure)
552 .map_err(|x| format!("creating async editor failed: {x}"))?;
553 tokio::spawn(async move {
554 loop {
555 // Wait until ForegroundAction::Readline finishes its task.
556 let permit = semaphore.acquire().await;
557 if permit.is_err() {
558 break;
559 };
560 // Let ForegroundAction::Readline decide when it's done.
561 permit.unwrap().forget();
562
563 // It's good to do readline now.
564 let result = editor.readline().await;
565 let _ = tx.send(ForegroundActions::Readline(result)).await;
566 }
567 });
568 }
569
570 'foreground_actions: loop {
571 let m = rx.recv().await;
572
573 if m.is_none() {
574 break;
575 }
576
577 match m.unwrap() {
578 ForegroundActions::ConnectAllEnabledProfiles(device) => {
579 if context.lock().unwrap().adapter_ready {
580 context
581 .lock()
582 .unwrap()
583 .adapter_dbus
584 .as_mut()
585 .unwrap()
586 .connect_all_enabled_profiles(device);
587 } else {
588 println!("Adapter isn't ready to connect profiles.");
589 }
590 }
591 ForegroundActions::RunCallback(callback) => {
592 callback(context.clone());
593
594 // Break the loop as a non-interactive command is completed.
595 if command.is_some() {
596 break;
597 }
598 }
599 // Once adapter is ready, register callbacks, get the address and mark it as ready
600 ForegroundActions::RegisterAdapterCallback(adapter) => {
601 let cb_objpath: String =
602 format!("/org/chromium/bluetooth/client/{}/bluetooth_callback", adapter);
603 let conn_cb_objpath: String =
604 format!("/org/chromium/bluetooth/client/{}/bluetooth_conn_callback", adapter);
605 let suspend_cb_objpath: String =
606 format!("/org/chromium/bluetooth/client/{}/suspend_callback", adapter);
607 let scanner_cb_objpath: String =
608 format!("/org/chromium/bluetooth/client/{}/scanner_callback", adapter);
609 let advertiser_cb_objpath: String =
610 format!("/org/chromium/bluetooth/client/{}/advertising_set_callback", adapter);
611 let admin_cb_objpath: String =
612 format!("/org/chromium/bluetooth/client/{}/admin_callback", adapter);
613 let socket_manager_cb_objpath: String =
614 format!("/org/chromium/bluetooth/client/{}/socket_manager_callback", adapter);
615 let qa_cb_objpath: String =
616 format!("/org/chromium/bluetooth/client/{}/qa_manager_callback", adapter);
617 let media_cb_objpath: String =
618 format!("/org/chromium/bluetooth/client/{}/bluetooth_media_callback", adapter);
619 let telephony_cb_objpath: String = format!(
620 "/org/chromium/bluetooth/client/{}/bluetooth_telephony_callback",
621 adapter
622 );
623 let battery_cb_objpath: String =
624 format!("/org/chromium/bluetooth/client/{}/battery_manager_callback", adapter);
625
626 let dbus_connection = context.lock().unwrap().dbus_connection.clone();
627 let dbus_crossroads = context.lock().unwrap().dbus_crossroads.clone();
628
629 context
630 .lock()
631 .unwrap()
632 .adapter_dbus
633 .as_mut()
634 .unwrap()
635 .rpc
636 .register_callback(Box::new(BtCallback::new(
637 cb_objpath.clone(),
638 context.clone(),
639 dbus_connection.clone(),
640 dbus_crossroads.clone(),
641 )))
642 .await
643 .expect("D-Bus error on IBluetooth::RegisterCallback");
644 context
645 .lock()
646 .unwrap()
647 .adapter_dbus
648 .as_mut()
649 .unwrap()
650 .rpc
651 .register_connection_callback(Box::new(BtConnectionCallback::new(
652 conn_cb_objpath,
653 context.clone(),
654 dbus_connection.clone(),
655 dbus_crossroads.clone(),
656 )))
657 .await
658 .expect("D-Bus error on IBluetooth::RegisterConnectionCallback");
659
660 // Register callback listener for le-scan`commands.
661 let scanner_callback_id = context
662 .lock()
663 .unwrap()
664 .gatt_dbus
665 .as_mut()
666 .unwrap()
667 .rpc
668 .register_scanner_callback(Box::new(ScannerCallback::new(
669 scanner_cb_objpath.clone(),
670 context.clone(),
671 dbus_connection.clone(),
672 dbus_crossroads.clone(),
673 )))
674 .await
675 .expect("D-Bus error on IBluetoothGatt::RegisterScannerCallback");
676 context.lock().unwrap().scanner_callback_id = Some(scanner_callback_id);
677
678 let advertiser_callback_id = context
679 .lock()
680 .unwrap()
681 .gatt_dbus
682 .as_mut()
683 .unwrap()
684 .rpc
685 .register_advertiser_callback(Box::new(AdvertisingSetCallback::new(
686 advertiser_cb_objpath.clone(),
687 context.clone(),
688 dbus_connection.clone(),
689 dbus_crossroads.clone(),
690 )))
691 .await
692 .expect("D-Bus error on IBluetoothGatt::RegisterAdvertiserCallback");
693 context.lock().unwrap().advertiser_callback_id = Some(advertiser_callback_id);
694
695 let admin_callback_id = context
696 .lock()
697 .unwrap()
698 .admin_dbus
699 .as_mut()
700 .unwrap()
701 .rpc
702 .register_admin_policy_callback(Box::new(AdminCallback::new(
703 admin_cb_objpath.clone(),
704 dbus_connection.clone(),
705 dbus_crossroads.clone(),
706 )))
707 .await
708 .expect("D-Bus error on IBluetoothAdmin::RegisterAdminCallback");
709 context.lock().unwrap().admin_callback_id = Some(admin_callback_id);
710
711 let socket_manager_callback_id = context
712 .lock()
713 .unwrap()
714 .socket_manager_dbus
715 .as_mut()
716 .unwrap()
717 .rpc
718 .register_callback(Box::new(BtSocketManagerCallback::new(
719 socket_manager_cb_objpath.clone(),
720 context.clone(),
721 dbus_connection.clone(),
722 dbus_crossroads.clone(),
723 )))
724 .await
725 .expect("D-Bus error on IBluetoothSocketManager::RegisterCallback");
726 context.lock().unwrap().socket_manager_callback_id =
727 Some(socket_manager_callback_id);
728
729 let qa_callback_id = context
730 .lock()
731 .unwrap()
732 .qa_dbus
733 .as_mut()
734 .unwrap()
735 .rpc
736 .register_qa_callback(Box::new(QACallback::new(
737 qa_cb_objpath.clone(),
738 context.clone(),
739 dbus_connection.clone(),
740 dbus_crossroads.clone(),
741 )))
742 .await
743 .expect("D-Bus error on IBluetoothQA::RegisterCallback");
744 context.lock().unwrap().qa_callback_id = Some(qa_callback_id);
745
746 // When adapter is ready, Suspend API is also ready. Register as an observer.
747 // TODO(b/224606285): Implement suspend debug utils in btclient.
748 context.lock().unwrap().suspend_dbus.as_mut().unwrap().register_callback(Box::new(
749 SuspendCallback::new(
750 suspend_cb_objpath,
751 dbus_connection.clone(),
752 dbus_crossroads.clone(),
753 ),
754 ));
755
756 context
757 .lock()
758 .unwrap()
759 .media_dbus
760 .as_mut()
761 .unwrap()
762 .rpc
763 .register_callback(Box::new(MediaCallback::new(
764 media_cb_objpath,
765 context.clone(),
766 dbus_connection.clone(),
767 dbus_crossroads.clone(),
768 )))
769 .await
770 .expect("D-Bus error on IBluetoothMedia::RegisterCallback");
771
772 context
773 .lock()
774 .unwrap()
775 .telephony_dbus
776 .as_mut()
777 .unwrap()
778 .rpc
779 .register_telephony_callback(Box::new(TelephonyCallback::new(
780 telephony_cb_objpath,
781 context.clone(),
782 dbus_connection.clone(),
783 dbus_crossroads.clone(),
784 )))
785 .await
786 .expect("D-Bus error on IBluetoothMedia::RegisterTelephonyCallback");
787
788 context
789 .lock()
790 .unwrap()
791 .battery_manager_dbus
792 .as_mut()
793 .unwrap()
794 .rpc
795 .register_battery_callback(Box::new(BatteryManagerCallback::new(
796 battery_cb_objpath,
797 context.clone(),
798 dbus_connection.clone(),
799 dbus_crossroads.clone(),
800 )))
801 .await
802 .expect("D-Bus error on IBatteryManagerDBus::RegisterBatteryCallback");
803
804 context.lock().unwrap().adapter_ready = true;
805 let adapter_address = context.lock().unwrap().update_adapter_address();
806 context.lock().unwrap().update_bonded_devices();
807
808 print_info!("Adapter {} is ready", adapter_address.to_string());
809
810 if run_command_on_ready {
811 if let Some(command) = command.as_ref() {
812 let mut iter = command.split(' ').map(String::from);
813 let first = iter.next().unwrap_or(String::from(""));
814 if !handler.process_cmd_line(&first, &iter.collect::<Vec<String>>()) {
815 // Break immediately if the command fails to execute.
816 break;
817 }
818
819 // Break the loop immediately if there is no callback
820 // to wait for.
821 if !context.lock().unwrap().client_commands_with_callbacks.contains(&first)
822 {
823 break;
824 }
825 }
826 }
827 }
828 ForegroundActions::Readline(result) => match result {
829 Err(rustyline::error::ReadlineError::Interrupted) => {
830 // Ctrl-C cancels the currently typed line, do nothing and ready to do next
831 // readline again.
832 semaphore_fg.add_permits(1);
833 }
834 Err(_err) => {
835 break;
836 }
837 Ok(line) => {
838 'readline: {
839 let args = match shell_words::split(line.as_str()) {
840 Ok(words) => words,
841 Err(e) => {
842 print_error!("Error parsing arguments: {}", e);
843 break 'readline;
844 }
845 };
846
847 let (cmd, rest) = match args.split_first() {
848 Some(pair) => pair,
849 None => break 'readline,
850 };
851
852 if cmd == "quit" {
853 break 'foreground_actions;
854 }
855
856 handler.process_cmd_line(cmd, rest);
857 break 'readline;
858 }
859
860 // Ready to do readline again.
861 semaphore_fg.add_permits(1);
862 }
863 },
864 }
865 }
866
867 semaphore_fg.close();
868
869 print_info!("Client exiting");
870 Ok(())
871 }
872