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