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