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