1 use std::collections::HashMap;
2 use std::fmt::{Display, Formatter};
3 use std::slice::SliceIndex;
4 use std::sync::{Arc, Mutex};
5 use std::time::Duration;
6
7 use crate::bt_adv::AdvSet;
8 use crate::bt_gatt::AuthReq;
9 use crate::callbacks::{BtGattCallback, BtGattServerCallback};
10 use crate::ClientContext;
11 use crate::{console_red, console_yellow, print_error, print_info};
12 use bt_topshim::btif::{BtConnectionState, BtDiscMode, BtStatus, BtTransport};
13 use bt_topshim::profiles::hid_host::BthhReportType;
14 use bt_topshim::profiles::sdp::{BtSdpMpsRecord, BtSdpRecord};
15 use bt_topshim::profiles::{gatt::LePhy, ProfileConnectionState};
16 use btstack::bluetooth::{BluetoothDevice, IBluetooth, IBluetoothQALegacy};
17 use btstack::bluetooth_gatt::{GattWriteType, IBluetoothGatt, ScanSettings, ScanType};
18 use btstack::bluetooth_media::IBluetoothTelephony;
19 use btstack::bluetooth_qa::IBluetoothQA;
20 use btstack::socket_manager::{IBluetoothSocketManager, SocketResult};
21 use btstack::uuid::{Profile, UuidHelper, UuidWrapper};
22 use manager_service::iface_bluetooth_manager::IBluetoothManager;
23
24 const INDENT_CHAR: &str = " ";
25 const BAR1_CHAR: &str = "=";
26 const BAR2_CHAR: &str = "-";
27 const MAX_MENU_CHAR_WIDTH: usize = 72;
28 const GATT_CLIENT_APP_UUID: &str = "12345678123456781234567812345678";
29 const GATT_SERVER_APP_UUID: &str = "12345678123456781234567812345679";
30
31 enum CommandError {
32 // Command not handled due to invalid arguments.
33 InvalidArgs,
34 // Command handled but failed with the given reason.
35 Failed(String),
36 }
37
38 impl From<&str> for CommandError {
from(s: &str) -> CommandError39 fn from(s: &str) -> CommandError {
40 CommandError::Failed(String::from(s))
41 }
42 }
43
44 impl From<String> for CommandError {
from(s: String) -> CommandError45 fn from(s: String) -> CommandError {
46 CommandError::Failed(s)
47 }
48 }
49
50 type CommandResult = Result<(), CommandError>;
51
52 type CommandFunction = fn(&mut CommandHandler, &Vec<String>) -> CommandResult;
53
_noop(_handler: &mut CommandHandler, _args: &Vec<String>) -> CommandResult54 fn _noop(_handler: &mut CommandHandler, _args: &Vec<String>) -> CommandResult {
55 // Used so we can add options with no direct function
56 // e.g. help and quit
57 Ok(())
58 }
59
60 pub struct CommandOption {
61 rules: Vec<String>,
62 description: String,
63 function_pointer: CommandFunction,
64 }
65
66 /// Handles string command entered from command line.
67 pub(crate) struct CommandHandler {
68 context: Arc<Mutex<ClientContext>>,
69 command_options: HashMap<String, CommandOption>,
70 }
71
72 /// Define what to do when a socket connects. Mainly for qualification purposes.
73 /// Specifically, after a socket is connected/accepted, we will do
74 /// (1) send a chunk of data every |send_interval| time until |num_frame| chunks has been sent.
75 /// (2) wait another |disconnect_delay| time. any incoming data will be dumpted during this time.
76 /// (3) disconnect the socket.
77 #[derive(Copy, Clone)]
78 pub struct SocketSchedule {
79 /// Number of times to send data
80 pub num_frame: u32,
81 /// Time interval between each sending
82 pub send_interval: Duration,
83 /// Extra time after the last sending. Any incoming data will be printed during this time.
84 pub disconnect_delay: Duration,
85 }
86
87 struct DisplayList<T>(Vec<T>);
88
89 impl<T: Display> Display for DisplayList<T> {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result90 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91 let _ = write!(f, "[\n");
92 for item in self.0.iter() {
93 let _ = write!(f, " {}\n", item);
94 }
95
96 write!(f, "]")
97 }
98 }
99
wrap_help_text(text: &str, max: usize, indent: usize) -> String100 fn wrap_help_text(text: &str, max: usize, indent: usize) -> String {
101 let remaining_count = std::cmp::max(
102 // real_max
103 std::cmp::max(max, text.chars().count())
104 // take away char count
105 - text.chars().count()
106 // take away real_indent
107 - (
108 if std::cmp::max(max, text.chars().count())- text.chars().count() > indent {
109 indent
110 } else {
111 0
112 }),
113 0,
114 );
115
116 format!("|{}{}{}|", INDENT_CHAR.repeat(indent), text, INDENT_CHAR.repeat(remaining_count))
117 }
118
119 // This should be called during the constructor in order to populate the command option map
build_commands() -> HashMap<String, CommandOption>120 fn build_commands() -> HashMap<String, CommandOption> {
121 let mut command_options = HashMap::<String, CommandOption>::new();
122 command_options.insert(
123 String::from("adapter"),
124 CommandOption {
125 rules: vec![
126 String::from("adapter enable"),
127 String::from("adapter disable"),
128 String::from("adapter show"),
129 String::from("adapter discoverable <on|limited|off> <duration>"),
130 String::from("adapter connectable <on|off>"),
131 String::from("adapter set-name <name>"),
132 ],
133 description: String::from(
134 "Enable/Disable/Show default bluetooth adapter. (e.g. adapter enable)\n
135 Discoverable On/Limited/Off (e.g. adapter discoverable on 60)\n
136 Connectable On/Off (e.g. adapter connectable on)",
137 ),
138 function_pointer: CommandHandler::cmd_adapter,
139 },
140 );
141 command_options.insert(
142 String::from("bond"),
143 CommandOption {
144 rules: vec![String::from("bond <add|remove|cancel> <address>")],
145 description: String::from("Creates a bond with a device."),
146 function_pointer: CommandHandler::cmd_bond,
147 },
148 );
149 command_options.insert(
150 String::from("device"),
151 CommandOption {
152 rules: vec![
153 String::from("device <connect|disconnect|info> <address>"),
154 String::from("device set-pairing-confirmation <address> <accept|reject>"),
155 String::from("device set-pairing-pin <address> <pin|reject>"),
156 String::from("device set-pairing-passkey <address> <passkey|reject>"),
157 String::from("device set-alias <address> <new-alias>"),
158 ],
159 description: String::from("Take action on a remote device. (i.e. info)"),
160 function_pointer: CommandHandler::cmd_device,
161 },
162 );
163 command_options.insert(
164 String::from("discovery"),
165 CommandOption {
166 rules: vec![String::from("discovery <start|stop>")],
167 description: String::from("Start and stop device discovery. (e.g. discovery start)"),
168 function_pointer: CommandHandler::cmd_discovery,
169 },
170 );
171 command_options.insert(
172 String::from("floss"),
173 CommandOption {
174 rules: vec![String::from("floss <enable|disable>")],
175 description: String::from("Enable or disable Floss for dogfood."),
176 function_pointer: CommandHandler::cmd_floss,
177 },
178 );
179 command_options.insert(
180 String::from("gatt"),
181 CommandOption {
182 rules: vec![
183 String::from("gatt register-client"),
184 String::from("gatt client-connect <address>"),
185 String::from("gatt client-read-phy <address>"),
186 String::from("gatt client-discover-services <address>"),
187 String::from("gatt client-discover-service-by-uuid-pts <address> <uuid>"),
188 String::from("gatt client-disconnect <address>"),
189 String::from("gatt configure-mtu <address> <mtu>"),
190 String::from("gatt set-direct-connect <true|false>"),
191 String::from("gatt set-connect-transport <Bredr|LE|Auto>"),
192 String::from("gatt set-connect-opportunistic <true|false>"),
193 String::from("gatt set-connect-phy <Phy1m|Phy2m|PhyCoded>"),
194 String::from("gatt set-auth-req <NONE|EncNoMitm|EncMitm|SignedNoMitm|SignedMitm>"),
195 String::from(
196 "gatt write-characteristic <address> <handle> <NoRsp|Write|Prepare> <value>",
197 ),
198 String::from("gatt read-characteristic <address> <handle>"),
199 String::from(
200 "gatt read-characteristic-by-uuid <address> <uuid> <start_handle> <end_handle>",
201 ),
202 String::from("gatt register-notification <address> <handle> <enable|disable>"),
203 String::from("gatt register-server"),
204 ],
205 description: String::from("GATT tools"),
206 function_pointer: CommandHandler::cmd_gatt,
207 },
208 );
209 command_options.insert(
210 String::from("le-scan"),
211 CommandOption {
212 rules: vec![
213 String::from("le-scan register-scanner"),
214 String::from("le-scan unregister-scanner <scanner-id>"),
215 String::from("le-scan start-scan <scanner-id>"),
216 String::from("le-scan stop-scan <scanner-id>"),
217 ],
218 description: String::from("LE scanning utilities."),
219 function_pointer: CommandHandler::cmd_le_scan,
220 },
221 );
222 command_options.insert(
223 String::from("advertise"),
224 CommandOption {
225 rules: vec![
226 String::from("advertise <on|off|ext>"),
227 String::from("advertise set-interval <ms>"),
228 String::from("advertise set-scan-rsp <enable|disable>"),
229 String::from("advertise set-raw-data <raw-adv-data> <adv-id>"),
230 String::from("advertise set-connectable <on|off> <adv-id>"),
231 ],
232 description: String::from("Advertising utilities."),
233 function_pointer: CommandHandler::cmd_advertise,
234 },
235 );
236 command_options.insert(
237 String::from("sdp"),
238 CommandOption {
239 rules: vec![String::from("sdp search <address> <uuid>")],
240 description: String::from("Service Discovery Protocol utilities."),
241 function_pointer: CommandHandler::cmd_sdp,
242 },
243 );
244 command_options.insert(
245 String::from("socket"),
246 CommandOption {
247 rules: vec![
248 String::from("socket listen <auth-required> <Bredr|LE>"),
249 String::from("socket listen-rfcomm <scn>"),
250 String::from("socket send-msc <dlci> <address>"),
251 String::from(
252 "socket connect <address> <l2cap|rfcomm> <psm|uuid> <auth-required> <Bredr|LE>",
253 ),
254 String::from("socket disconnect <socket_id>"),
255 String::from("socket set-on-connect-schedule <send|resend|dump>"),
256 ],
257 description: String::from("Socket manager utilities."),
258 function_pointer: CommandHandler::cmd_socket,
259 },
260 );
261 command_options.insert(
262 String::from("hid"),
263 CommandOption {
264 rules: vec![
265 String::from("hid get-report <address> <Input|Output|Feature> <report_id>"),
266 String::from("hid set-report <address> <Input|Output|Feature> <report_value>"),
267 String::from("hid send-data <address> <data>"),
268 ],
269 description: String::from("Socket manager utilities."),
270 function_pointer: CommandHandler::cmd_hid,
271 },
272 );
273 command_options.insert(
274 String::from("get-address"),
275 CommandOption {
276 rules: vec![String::from("get-address")],
277 description: String::from("Gets the local device address."),
278 function_pointer: CommandHandler::cmd_get_address,
279 },
280 );
281 command_options.insert(
282 String::from("qa"),
283 CommandOption {
284 rules: vec![String::from("qa add-media-player <name> <browsing_supported>")],
285 description: String::from("Methods for testing purposes"),
286 function_pointer: CommandHandler::cmd_qa,
287 },
288 );
289 command_options.insert(
290 String::from("help"),
291 CommandOption {
292 rules: vec![String::from("help")],
293 description: String::from("Shows this menu."),
294 function_pointer: CommandHandler::cmd_help,
295 },
296 );
297 command_options.insert(
298 String::from("list"),
299 CommandOption {
300 rules: vec![String::from("list <bonded|found|connected>")],
301 description: String::from(
302 "List bonded or found remote devices. Use: list <bonded|found>",
303 ),
304 function_pointer: CommandHandler::cmd_list_devices,
305 },
306 );
307 command_options.insert(
308 String::from("telephony"),
309 CommandOption {
310 rules: vec![
311 String::from("telephony set-network <on|off>"),
312 String::from("telephony set-roaming <on|off>"),
313 String::from("telephony set-signal <strength>"),
314 String::from("telephony set-battery <level>"),
315 String::from("telephony <enable|disable>"),
316 String::from("telephony <incoming-call|dialing-call> <number>"),
317 String::from("telephony <answer-call|hangup-call>"),
318 String::from("telephony <set-memory-call|set-last-call> [<number>]"),
319 String::from(
320 "telephony <release-held|release-active-accept-held|hold-active-accept-held>",
321 ),
322 String::from("telephony <audio-connect|audio-disconnect> <address>"),
323 ],
324 description: String::from("Set device telephony status."),
325 function_pointer: CommandHandler::cmd_telephony,
326 },
327 );
328 command_options.insert(
329 String::from("quit"),
330 CommandOption {
331 rules: vec![String::from("quit")],
332 description: String::from("Quit out of the interactive shell."),
333 function_pointer: _noop,
334 },
335 );
336 command_options
337 }
338
339 // Helper to index a vector safely. The same as `args.get(i)` but converts the None into a
340 // CommandError::InvalidArgs.
341 //
342 // Use this to safely index an argument and conveniently return the error if the argument does not
343 // exist.
get_arg<I>( args: &Vec<String>, index: I, ) -> Result<&<I as SliceIndex<[String]>>::Output, CommandError> where I: SliceIndex<[String]>,344 fn get_arg<I>(
345 args: &Vec<String>,
346 index: I,
347 ) -> Result<&<I as SliceIndex<[String]>>::Output, CommandError>
348 where
349 I: SliceIndex<[String]>,
350 {
351 args.get(index).ok_or(CommandError::InvalidArgs)
352 }
353
354 impl CommandHandler {
355 /// Creates a new CommandHandler.
new(context: Arc<Mutex<ClientContext>>) -> CommandHandler356 pub fn new(context: Arc<Mutex<ClientContext>>) -> CommandHandler {
357 CommandHandler { context, command_options: build_commands() }
358 }
359
360 /// Entry point for command and arguments
process_cmd_line(&mut self, command: &str, args: &Vec<String>)361 pub fn process_cmd_line(&mut self, command: &str, args: &Vec<String>) {
362 // Ignore empty line
363 match command {
364 "" => {}
365 _ => match self.command_options.get(command) {
366 Some(cmd) => {
367 let rules = cmd.rules.clone();
368 match (cmd.function_pointer)(self, &args) {
369 Ok(()) => {}
370 Err(CommandError::InvalidArgs) => {
371 print_error!("Invalid arguments. Usage:\n{}", rules.join("\n"));
372 }
373 Err(CommandError::Failed(msg)) => {
374 print_error!("Command failed: {}", msg);
375 }
376 }
377 }
378 None => {
379 println!("'{}' is an invalid command!", command);
380 self.cmd_help(&args).ok();
381 }
382 },
383 };
384 }
385
lock_context(&self) -> std::sync::MutexGuard<ClientContext>386 fn lock_context(&self) -> std::sync::MutexGuard<ClientContext> {
387 self.context.lock().unwrap()
388 }
389
390 // Common message for when the adapter isn't ready
adapter_not_ready(&self) -> CommandError391 fn adapter_not_ready(&self) -> CommandError {
392 format!(
393 "Default adapter {} is not enabled. Enable the adapter before using this command.",
394 self.lock_context().default_adapter
395 )
396 .into()
397 }
398
cmd_help(&mut self, args: &Vec<String>) -> CommandResult399 fn cmd_help(&mut self, args: &Vec<String>) -> CommandResult {
400 if let Some(command) = args.get(0) {
401 match self.command_options.get(command) {
402 Some(cmd) => {
403 println!(
404 "\n{}{}\n{}{}\n",
405 INDENT_CHAR.repeat(4),
406 command,
407 INDENT_CHAR.repeat(8),
408 cmd.description
409 );
410 }
411 None => {
412 println!("'{}' is an invalid command!", command);
413 self.cmd_help(&vec![]).ok();
414 }
415 }
416 } else {
417 // Build equals bar and Shave off sides
418 let equal_bar = format!(" {} ", BAR1_CHAR.repeat(MAX_MENU_CHAR_WIDTH));
419
420 // Build empty bar and Shave off sides
421 let empty_bar = format!("|{}|", INDENT_CHAR.repeat(MAX_MENU_CHAR_WIDTH));
422
423 // Header
424 println!(
425 "\n{}\n{}\n{}\n{}",
426 equal_bar,
427 wrap_help_text("Help Menu", MAX_MENU_CHAR_WIDTH, 2),
428 // Minus bar
429 format!("+{}+", BAR2_CHAR.repeat(MAX_MENU_CHAR_WIDTH)),
430 empty_bar
431 );
432
433 // Print commands
434 for (key, val) in self.command_options.iter() {
435 println!(
436 "{}\n{}\n{}",
437 wrap_help_text(&key, MAX_MENU_CHAR_WIDTH, 4),
438 wrap_help_text(&val.description, MAX_MENU_CHAR_WIDTH, 8),
439 empty_bar
440 );
441 }
442
443 // Footer
444 println!("{}\n{}", empty_bar, equal_bar);
445 }
446
447 Ok(())
448 }
449
cmd_adapter(&mut self, args: &Vec<String>) -> CommandResult450 fn cmd_adapter(&mut self, args: &Vec<String>) -> CommandResult {
451 if !self.lock_context().manager_dbus.get_floss_enabled() {
452 return Err("Floss is not enabled. First run, `floss enable`".into());
453 }
454
455 let default_adapter = self.lock_context().default_adapter;
456
457 let command = get_arg(args, 0)?;
458
459 match &command[..] {
460 "enable" => {
461 if self.lock_context().is_restricted {
462 return Err("You are not allowed to toggle adapter power".into());
463 }
464 self.lock_context().manager_dbus.start(default_adapter);
465 }
466 "disable" => {
467 if self.lock_context().is_restricted {
468 return Err("You are not allowed to toggle adapter power".into());
469 }
470 self.lock_context().manager_dbus.stop(default_adapter);
471 }
472 "show" => {
473 if !self.lock_context().adapter_ready {
474 return Err(self.adapter_not_ready());
475 }
476
477 let enabled = self.lock_context().enabled;
478 let address = match self.lock_context().adapter_address.as_ref() {
479 Some(x) => x.clone(),
480 None => String::from(""),
481 };
482 let context = self.lock_context();
483 let adapter_dbus = context.adapter_dbus.as_ref().unwrap();
484 let qa_legacy_dbus = context.qa_legacy_dbus.as_ref().unwrap();
485 let qa_dbus = context.qa_dbus.as_ref().unwrap();
486 let name = adapter_dbus.get_name();
487 let uuids = adapter_dbus.get_uuids();
488 let is_discoverable = adapter_dbus.get_discoverable();
489 let is_connectable = qa_legacy_dbus.get_connectable();
490 let alias = qa_legacy_dbus.get_alias();
491 let modalias = qa_legacy_dbus.get_modalias();
492 let discoverable_mode = qa_dbus.get_discoverable_mode();
493 let discoverable_timeout = adapter_dbus.get_discoverable_timeout();
494 let cod = adapter_dbus.get_bluetooth_class();
495 let multi_adv_supported = adapter_dbus.is_multi_advertisement_supported();
496 let le_ext_adv_supported = adapter_dbus.is_le_extended_advertising_supported();
497 let wbs_supported = adapter_dbus.is_wbs_supported();
498 let supported_profiles = UuidHelper::get_supported_profiles();
499 let connected_profiles: Vec<(Profile, ProfileConnectionState)> = supported_profiles
500 .iter()
501 .map(|&prof| {
502 if let Some(uuid) = UuidHelper::get_profile_uuid(&prof) {
503 (prof, adapter_dbus.get_profile_connection_state(uuid.clone()))
504 } else {
505 (prof, ProfileConnectionState::Disconnected)
506 }
507 })
508 .filter(|(_prof, state)| state != &ProfileConnectionState::Disconnected)
509 .collect();
510 print_info!("Address: {}", address);
511 print_info!("Name: {}", name);
512 print_info!("Alias: {}", alias);
513 print_info!("Modalias: {}", modalias);
514 print_info!("State: {}", if enabled { "enabled" } else { "disabled" });
515 print_info!("Discoverable: {}", is_discoverable);
516 print_info!("Discoverable mode: {:?}", discoverable_mode);
517 print_info!("DiscoverableTimeout: {}s", discoverable_timeout);
518 print_info!("Connectable: {}", is_connectable);
519 print_info!("Class: {:#06x}", cod);
520 print_info!("IsMultiAdvertisementSupported: {}", multi_adv_supported);
521 print_info!("IsLeExtendedAdvertisingSupported: {}", le_ext_adv_supported);
522 print_info!("Connected profiles: {:?}", connected_profiles);
523 print_info!("IsWbsSupported: {}", wbs_supported);
524 print_info!(
525 "Uuids: {}",
526 DisplayList(
527 uuids
528 .iter()
529 .map(|&x| UuidHelper::known_uuid_to_string(&x))
530 .collect::<Vec<String>>()
531 )
532 );
533 }
534 "discoverable" => match &get_arg(args, 1)?[..] {
535 "on" => {
536 let duration = String::from(get_arg(args, 2)?)
537 .parse::<u32>()
538 .or(Err("Failed parsing duration."))?;
539
540 let discoverable = self
541 .lock_context()
542 .adapter_dbus
543 .as_mut()
544 .unwrap()
545 .set_discoverable(BtDiscMode::GeneralDiscoverable, duration);
546 print_info!(
547 "Set discoverable for {} seconds: {}",
548 duration,
549 if discoverable { "succeeded" } else { "failed" }
550 );
551 }
552 "limited" => {
553 let duration = String::from(get_arg(args, 2)?)
554 .parse::<u32>()
555 .or(Err("Failed parsing duration."))?;
556
557 let discoverable = self
558 .lock_context()
559 .adapter_dbus
560 .as_mut()
561 .unwrap()
562 .set_discoverable(BtDiscMode::LimitedDiscoverable, duration);
563 print_info!(
564 "Set limited discoverable for {} seconds: {}",
565 duration,
566 if discoverable { "succeeded" } else { "failed" }
567 );
568 }
569 "off" => {
570 let discoverable = self
571 .lock_context()
572 .adapter_dbus
573 .as_mut()
574 .unwrap()
575 .set_discoverable(BtDiscMode::NonDiscoverable, 0 /*not used*/);
576 print_info!(
577 "Turn discoverable off: {}",
578 if discoverable { "succeeded" } else { "failed" }
579 );
580 }
581 other => println!("Invalid argument for adapter discoverable '{}'", other),
582 },
583 "connectable" => match &get_arg(args, 1)?[..] {
584 "on" => {
585 let ret =
586 self.lock_context().qa_legacy_dbus.as_mut().unwrap().set_connectable(true);
587 print_info!("Set connectable on {}", if ret { "succeeded" } else { "failed" });
588 }
589 "off" => {
590 let ret =
591 self.lock_context().qa_legacy_dbus.as_mut().unwrap().set_connectable(false);
592 print_info!("Set connectable off {}", if ret { "succeeded" } else { "failed" });
593 }
594 other => println!("Invalid argument for adapter connectable '{}'", other),
595 },
596 "set-name" => {
597 if let Some(name) = args.get(1) {
598 self.lock_context().adapter_dbus.as_ref().unwrap().set_name(name.to_string());
599 } else {
600 println!("usage: adapter set-name <name>");
601 }
602 }
603
604 _ => return Err(CommandError::InvalidArgs),
605 };
606
607 Ok(())
608 }
609
cmd_get_address(&mut self, _args: &Vec<String>) -> CommandResult610 fn cmd_get_address(&mut self, _args: &Vec<String>) -> CommandResult {
611 if !self.lock_context().adapter_ready {
612 return Err(self.adapter_not_ready());
613 }
614
615 let address = self.lock_context().update_adapter_address();
616 print_info!("Local address = {}", &address);
617 Ok(())
618 }
619
cmd_discovery(&mut self, args: &Vec<String>) -> CommandResult620 fn cmd_discovery(&mut self, args: &Vec<String>) -> CommandResult {
621 if !self.lock_context().adapter_ready {
622 return Err(self.adapter_not_ready());
623 }
624
625 let command = get_arg(args, 0)?;
626
627 match &command[..] {
628 "start" => {
629 self.lock_context().adapter_dbus.as_mut().unwrap().start_discovery();
630 }
631 "stop" => {
632 self.lock_context().adapter_dbus.as_mut().unwrap().cancel_discovery();
633 }
634 _ => return Err(CommandError::InvalidArgs),
635 }
636
637 Ok(())
638 }
639
cmd_bond(&mut self, args: &Vec<String>) -> CommandResult640 fn cmd_bond(&mut self, args: &Vec<String>) -> CommandResult {
641 if !self.lock_context().adapter_ready {
642 return Err(self.adapter_not_ready());
643 }
644
645 let command = get_arg(args, 0)?;
646
647 match &command[..] {
648 "add" => {
649 let device = BluetoothDevice {
650 address: String::from(get_arg(args, 1)?),
651 name: String::from("Classic Device"),
652 };
653
654 let bonding_attempt = &self.lock_context().bonding_attempt.as_ref().cloned();
655
656 if bonding_attempt.is_some() {
657 return Err(format!(
658 "Already bonding [{}]. Cancel bonding first.",
659 bonding_attempt.as_ref().unwrap().address,
660 )
661 .into());
662 }
663
664 let success = self
665 .lock_context()
666 .adapter_dbus
667 .as_mut()
668 .unwrap()
669 .create_bond(device.clone(), BtTransport::Auto);
670
671 if success {
672 self.lock_context().bonding_attempt = Some(device);
673 }
674 }
675 "remove" => {
676 let device = BluetoothDevice {
677 address: String::from(get_arg(args, 1)?),
678 name: String::from("Classic Device"),
679 };
680
681 self.lock_context().adapter_dbus.as_ref().unwrap().remove_bond(device);
682 }
683 "cancel" => {
684 let device = BluetoothDevice {
685 address: String::from(get_arg(args, 1)?),
686 name: String::from("Classic Device"),
687 };
688
689 self.lock_context().adapter_dbus.as_ref().unwrap().cancel_bond_process(device);
690 }
691 other => {
692 println!("Invalid argument '{}'", other);
693 }
694 }
695
696 Ok(())
697 }
698
cmd_device(&mut self, args: &Vec<String>) -> CommandResult699 fn cmd_device(&mut self, args: &Vec<String>) -> CommandResult {
700 if !self.lock_context().adapter_ready {
701 return Err(self.adapter_not_ready());
702 }
703
704 let command = &get_arg(args, 0)?;
705
706 match &command[..] {
707 "connect" => {
708 let device = BluetoothDevice {
709 address: String::from(get_arg(args, 1)?),
710 name: String::from("Classic Device"),
711 };
712
713 let success = self
714 .lock_context()
715 .adapter_dbus
716 .as_mut()
717 .unwrap()
718 .connect_all_enabled_profiles(device.clone());
719
720 if success {
721 println!("Connecting to {}", &device.address);
722 } else {
723 println!("Can't connect to {}", &device.address);
724 }
725 }
726 "disconnect" => {
727 let device = BluetoothDevice {
728 address: String::from(get_arg(args, 1)?),
729 name: String::from("Classic Device"),
730 };
731
732 let success = self
733 .lock_context()
734 .adapter_dbus
735 .as_mut()
736 .unwrap()
737 .disconnect_all_enabled_profiles(device.clone());
738
739 if success {
740 println!("Disconnecting from {}", &device.address);
741 } else {
742 println!("Can't disconnect from {}", &device.address);
743 }
744 }
745 "info" => {
746 let device = BluetoothDevice {
747 address: String::from(get_arg(args, 1)?),
748 name: String::from("Classic Device"),
749 };
750
751 let (
752 name,
753 alias,
754 device_type,
755 class,
756 appearance,
757 bonded,
758 connection_state,
759 uuids,
760 wake_allowed,
761 ) = {
762 let ctx = self.lock_context();
763 let adapter = ctx.adapter_dbus.as_ref().unwrap();
764
765 let name = adapter.get_remote_name(device.clone());
766 let device_type = adapter.get_remote_type(device.clone());
767 let alias = adapter.get_remote_alias(device.clone());
768 let class = adapter.get_remote_class(device.clone());
769 let appearance = adapter.get_remote_appearance(device.clone());
770 let bonded = adapter.get_bond_state(device.clone());
771 let connection_state = match adapter.get_connection_state(device.clone()) {
772 BtConnectionState::NotConnected => "Not Connected",
773 BtConnectionState::ConnectedOnly => "Connected",
774 _ => "Connected and Paired",
775 };
776 let uuids = adapter.get_remote_uuids(device.clone());
777 let wake_allowed = adapter.get_remote_wake_allowed(device.clone());
778
779 (
780 name,
781 alias,
782 device_type,
783 class,
784 appearance,
785 bonded,
786 connection_state,
787 uuids,
788 wake_allowed,
789 )
790 };
791
792 print_info!("Address: {}", &device.address);
793 print_info!("Name: {}", name);
794 print_info!("Alias: {}", alias);
795 print_info!("Type: {:?}", device_type);
796 print_info!("Class: {}", class);
797 print_info!("Appearance: {}", appearance);
798 print_info!("Wake Allowed: {}", wake_allowed);
799 print_info!("Bond State: {:?}", bonded);
800 print_info!("Connection State: {}", connection_state);
801 print_info!(
802 "Uuids: {}",
803 DisplayList(
804 uuids
805 .iter()
806 .map(|&x| UuidHelper::known_uuid_to_string(&x))
807 .collect::<Vec<String>>()
808 )
809 );
810 }
811 "set-alias" => {
812 let new_alias = get_arg(args, 2)?;
813 let device = BluetoothDevice {
814 address: String::from(get_arg(args, 1)?),
815 name: String::from(""),
816 };
817 let old_alias = self
818 .lock_context()
819 .adapter_dbus
820 .as_ref()
821 .unwrap()
822 .get_remote_alias(device.clone());
823 println!(
824 "Updating alias for {}: {} -> {}",
825 get_arg(args, 1)?,
826 old_alias,
827 new_alias
828 );
829 self.lock_context()
830 .adapter_dbus
831 .as_mut()
832 .unwrap()
833 .set_remote_alias(device.clone(), new_alias.clone());
834 }
835 "set-pairing-confirmation" => {
836 let device = BluetoothDevice {
837 address: String::from(get_arg(args, 1)?),
838 name: String::from(""),
839 };
840 let accept = match &get_arg(args, 2)?[..] {
841 "accept" => true,
842 "reject" => false,
843 other => {
844 return Err(format!("Failed to parse '{}'", other).into());
845 }
846 };
847
848 self.lock_context()
849 .adapter_dbus
850 .as_mut()
851 .unwrap()
852 .set_pairing_confirmation(device.clone(), accept);
853 }
854 "set-pairing-pin" => {
855 let device = BluetoothDevice {
856 address: String::from(get_arg(args, 1)?),
857 name: String::from(""),
858 };
859 let pin = get_arg(args, 2)?;
860
861 let (accept, pin) = match (&pin[..], pin) {
862 ("reject", _) => (false, vec![]),
863 (_, p) => (true, p.as_bytes().iter().cloned().collect::<Vec<u8>>()),
864 };
865
866 self.lock_context().adapter_dbus.as_mut().unwrap().set_pin(
867 device.clone(),
868 accept,
869 pin,
870 );
871 }
872 "set-pairing-passkey" => {
873 let device = BluetoothDevice {
874 address: String::from(get_arg(args, 1)?),
875 name: String::from(""),
876 };
877 let passkey = get_arg(args, 2)?;
878 let (accept, passkey) = match (&passkey[..], String::from(passkey).parse::<u32>()) {
879 (_, Ok(p)) => (true, Vec::from(p.to_ne_bytes())),
880 ("reject", _) => (false, vec![]),
881 _ => {
882 return Err(format!("Failed to parse '{}'", passkey).into());
883 }
884 };
885
886 self.lock_context().adapter_dbus.as_mut().unwrap().set_passkey(
887 device.clone(),
888 accept,
889 passkey,
890 );
891 }
892 other => {
893 println!("Invalid argument '{}'", other);
894 }
895 }
896
897 Ok(())
898 }
899
cmd_floss(&mut self, args: &Vec<String>) -> CommandResult900 fn cmd_floss(&mut self, args: &Vec<String>) -> CommandResult {
901 let command = get_arg(args, 0)?;
902
903 match &command[..] {
904 "enable" => {
905 self.lock_context().manager_dbus.set_floss_enabled(true);
906 }
907 "disable" => {
908 self.lock_context().manager_dbus.set_floss_enabled(false);
909 }
910 "show" => {
911 print_info!(
912 "Floss enabled: {}",
913 self.lock_context().manager_dbus.get_floss_enabled()
914 );
915 }
916 _ => return Err(CommandError::InvalidArgs),
917 }
918
919 Ok(())
920 }
921
cmd_gatt(&mut self, args: &Vec<String>) -> CommandResult922 fn cmd_gatt(&mut self, args: &Vec<String>) -> CommandResult {
923 if !self.lock_context().adapter_ready {
924 return Err(self.adapter_not_ready());
925 }
926
927 let command = get_arg(args, 0)?;
928
929 match &command[..] {
930 "register-client" => {
931 let dbus_connection = self.lock_context().dbus_connection.clone();
932 let dbus_crossroads = self.lock_context().dbus_crossroads.clone();
933
934 self.lock_context().gatt_dbus.as_mut().unwrap().register_client(
935 String::from(GATT_CLIENT_APP_UUID),
936 Box::new(BtGattCallback::new(
937 String::from("/org/chromium/bluetooth/client/bluetooth_gatt_callback"),
938 self.context.clone(),
939 dbus_connection,
940 dbus_crossroads,
941 )),
942 false,
943 );
944 }
945 "client-connect" => {
946 let client_id = self
947 .lock_context()
948 .gatt_client_context
949 .client_id
950 .ok_or("GATT client is not yet registered.")?;
951
952 let addr = String::from(get_arg(args, 1)?);
953 let is_direct = self.lock_context().gatt_client_context.is_connect_direct;
954 let transport = self.lock_context().gatt_client_context.connect_transport;
955 let oppurtunistic = self.lock_context().gatt_client_context.connect_opportunistic;
956 let phy = self.lock_context().gatt_client_context.connect_phy;
957
958 println!("Initiating GATT client connect. client_id: {}, addr: {}, is_direct: {}, transport: {:?}, oppurtunistic: {}, phy: {:?}", client_id, addr, is_direct, transport, oppurtunistic, phy);
959 self.lock_context().gatt_dbus.as_ref().unwrap().client_connect(
960 client_id,
961 addr,
962 is_direct,
963 transport,
964 oppurtunistic,
965 phy,
966 );
967 }
968 "client-disconnect" => {
969 let client_id = self
970 .lock_context()
971 .gatt_client_context
972 .client_id
973 .ok_or("GATT client is not yet registered.")?;
974
975 let addr = String::from(get_arg(args, 1)?);
976 self.lock_context().gatt_dbus.as_ref().unwrap().client_disconnect(client_id, addr);
977 }
978 "client-read-phy" => {
979 let client_id = self
980 .lock_context()
981 .gatt_client_context
982 .client_id
983 .ok_or("GATT client is not yet registered.")?;
984 let addr = String::from(get_arg(args, 1)?);
985 self.lock_context().gatt_dbus.as_mut().unwrap().client_read_phy(client_id, addr);
986 }
987 "client-discover-services" => {
988 let client_id = self
989 .lock_context()
990 .gatt_client_context
991 .client_id
992 .ok_or("GATT client is not yet registered.")?;
993
994 let addr = String::from(get_arg(args, 1)?);
995 self.lock_context().gatt_dbus.as_ref().unwrap().discover_services(client_id, addr);
996 }
997 "client-discover-service-by-uuid-pts" => {
998 let client_id = self
999 .lock_context()
1000 .gatt_client_context
1001 .client_id
1002 .ok_or("GATT client is not yet registered.")?;
1003 let addr = String::from(get_arg(args, 1)?);
1004 let uuid = String::from(get_arg(args, 2)?);
1005 self.lock_context()
1006 .gatt_dbus
1007 .as_ref()
1008 .unwrap()
1009 .btif_gattc_discover_service_by_uuid(client_id, addr, uuid);
1010 }
1011 "configure-mtu" => {
1012 let client_id = self
1013 .lock_context()
1014 .gatt_client_context
1015 .client_id
1016 .ok_or("GATT client is not yet registered.")?;
1017
1018 let addr = String::from(get_arg(args, 1)?);
1019 let mtu =
1020 String::from(get_arg(args, 2)?).parse::<i32>().or(Err("Failed parsing mtu"))?;
1021
1022 self.lock_context().gatt_dbus.as_ref().unwrap().configure_mtu(client_id, addr, mtu)
1023 }
1024 "set-direct-connect" => {
1025 let is_direct = String::from(get_arg(args, 1)?)
1026 .parse::<bool>()
1027 .or(Err("Failed to parse is_direct"))?;
1028
1029 self.lock_context().gatt_client_context.is_connect_direct = is_direct;
1030 }
1031 "set-connect-transport" => {
1032 let transport = match &get_arg(args, 1)?[..] {
1033 "Bredr" => BtTransport::Bredr,
1034 "LE" => BtTransport::Le,
1035 "Auto" => BtTransport::Auto,
1036 _ => {
1037 return Err("Failed to parse transport".into());
1038 }
1039 };
1040 self.lock_context().gatt_client_context.connect_transport = transport;
1041 }
1042 "set-connect-opportunistic" => {
1043 let opportunistic = String::from(get_arg(args, 1)?)
1044 .parse::<bool>()
1045 .or(Err("Failed to parse opportunistic"))?;
1046
1047 self.lock_context().gatt_client_context.connect_opportunistic = opportunistic;
1048 }
1049 "set-connect-phy" => {
1050 let phy = match &get_arg(args, 1)?[..] {
1051 "Phy1m" => LePhy::Phy1m,
1052 "Phy2m" => LePhy::Phy2m,
1053 "PhyCoded" => LePhy::PhyCoded,
1054 _ => {
1055 return Err("Failed to parse phy".into());
1056 }
1057 };
1058
1059 self.lock_context().gatt_client_context.connect_phy = phy;
1060 }
1061 "set-auth-req" => {
1062 let flag = match &get_arg(args, 1)?[..] {
1063 "NONE" => AuthReq::NONE,
1064 "EncNoMitm" => AuthReq::EncNoMitm,
1065 "EncMitm" => AuthReq::EncMitm,
1066 "SignedNoMitm" => AuthReq::SignedNoMitm,
1067 "SignedMitm" => AuthReq::SignedMitm,
1068 _ => {
1069 return Err("Failed to parse auth-req".into());
1070 }
1071 };
1072
1073 self.lock_context().gatt_client_context.auth_req = flag;
1074 println!("AuthReq: {:?}", self.lock_context().gatt_client_context.get_auth_req());
1075 }
1076 "write-characteristic" => {
1077 let addr = String::from(get_arg(args, 1)?);
1078 let handle = String::from(get_arg(args, 2)?)
1079 .parse::<i32>()
1080 .or(Err("Failed to parse handle"))?;
1081
1082 let write_type = match &get_arg(args, 3)?[..] {
1083 "NoRsp" => GattWriteType::WriteNoRsp,
1084 "Write" => GattWriteType::Write,
1085 "Prepare" => GattWriteType::WritePrepare,
1086 _ => {
1087 return Err("Failed to parse write-type".into());
1088 }
1089 };
1090
1091 let value = hex::decode(&get_arg(args, 4)?).or(Err("Failed to parse value"))?;
1092
1093 let client_id = self
1094 .lock_context()
1095 .gatt_client_context
1096 .client_id
1097 .ok_or("GATT client is not yet registered.")?;
1098
1099 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1100
1101 self.lock_context()
1102 .gatt_dbus
1103 .as_ref()
1104 .unwrap()
1105 .write_characteristic(client_id, addr, handle, write_type, auth_req, value);
1106 }
1107 "read-characteristic" => {
1108 let addr = String::from(get_arg(args, 1)?);
1109 let handle = String::from(get_arg(args, 2)?)
1110 .parse::<i32>()
1111 .or(Err("Failed to parse handle"))?;
1112 let client_id = self
1113 .lock_context()
1114 .gatt_client_context
1115 .client_id
1116 .ok_or("GATT client is not yet registered.")?;
1117
1118 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1119
1120 self.lock_context()
1121 .gatt_dbus
1122 .as_ref()
1123 .unwrap()
1124 .read_characteristic(client_id, addr, handle, auth_req);
1125 }
1126 "read-characteristic-by-uuid" => {
1127 let addr = String::from(get_arg(args, 1)?);
1128 let uuid = String::from(get_arg(args, 2)?);
1129 let start_handle = String::from(get_arg(args, 3)?)
1130 .parse::<i32>()
1131 .or(Err("Failed to parse start handle"))?;
1132 let end_handle = String::from(get_arg(args, 4)?)
1133 .parse::<i32>()
1134 .or(Err("Failed to parse end handle"))?;
1135
1136 let client_id = self
1137 .lock_context()
1138 .gatt_client_context
1139 .client_id
1140 .ok_or("GATT client is not yet registered.")?;
1141
1142 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1143
1144 self.lock_context().gatt_dbus.as_ref().unwrap().read_using_characteristic_uuid(
1145 client_id,
1146 addr,
1147 uuid,
1148 start_handle,
1149 end_handle,
1150 auth_req,
1151 );
1152 }
1153 "register-notification" => {
1154 let addr = String::from(get_arg(args, 1)?);
1155 let handle = String::from(get_arg(args, 2)?)
1156 .parse::<i32>()
1157 .or(Err("Failed to parse handle"))?;
1158 let enable = match &get_arg(args, 3)?[..] {
1159 "enable" => true,
1160 "disable" => false,
1161 _ => {
1162 return Err("Failed to parse enable".into());
1163 }
1164 };
1165
1166 let client_id = self
1167 .lock_context()
1168 .gatt_client_context
1169 .client_id
1170 .ok_or("GATT client is not yet registered.")?;
1171
1172 self.lock_context()
1173 .gatt_dbus
1174 .as_ref()
1175 .unwrap()
1176 .register_for_notification(client_id, addr, handle, enable);
1177 }
1178 "register-server" => {
1179 let dbus_connection = self.lock_context().dbus_connection.clone();
1180 let dbus_crossroads = self.lock_context().dbus_crossroads.clone();
1181
1182 self.lock_context().gatt_dbus.as_mut().unwrap().register_server(
1183 String::from(GATT_SERVER_APP_UUID),
1184 Box::new(BtGattServerCallback::new(
1185 String::from(
1186 "/org/chromium/bluetooth/client/bluetooth_gatt_server_callback",
1187 ),
1188 self.context.clone(),
1189 dbus_connection,
1190 dbus_crossroads,
1191 )),
1192 false,
1193 );
1194 }
1195 _ => return Err(CommandError::InvalidArgs),
1196 }
1197 Ok(())
1198 }
1199
cmd_le_scan(&mut self, args: &Vec<String>) -> CommandResult1200 fn cmd_le_scan(&mut self, args: &Vec<String>) -> CommandResult {
1201 if !self.lock_context().adapter_ready {
1202 return Err(self.adapter_not_ready());
1203 }
1204
1205 let command = get_arg(args, 0)?;
1206
1207 match &command[..] {
1208 "register-scanner" => {
1209 let scanner_callback_id = self
1210 .lock_context()
1211 .scanner_callback_id
1212 .ok_or("Cannot register scanner before registering scanner callback")?;
1213
1214 let uuid = self
1215 .lock_context()
1216 .gatt_dbus
1217 .as_mut()
1218 .unwrap()
1219 .register_scanner(scanner_callback_id);
1220
1221 print_info!("Scanner to be registered with UUID = {}", UuidWrapper(&uuid));
1222 }
1223 "unregister-scanner" => {
1224 let scanner_id = String::from(get_arg(args, 1)?)
1225 .parse::<u8>()
1226 .or(Err("Failed parsing scanner id"))?;
1227
1228 self.lock_context().gatt_dbus.as_mut().unwrap().unregister_scanner(scanner_id);
1229 }
1230 "start-scan" => {
1231 let scanner_id = String::from(get_arg(args, 1)?)
1232 .parse::<u8>()
1233 .or(Err("Failed parsing scanner id"))?;
1234
1235 self.lock_context().gatt_dbus.as_mut().unwrap().start_scan(
1236 scanner_id,
1237 // TODO(b/254870159): Construct real settings and filters depending on
1238 // command line options.
1239 ScanSettings { interval: 0, window: 0, scan_type: ScanType::Active },
1240 Some(btstack::bluetooth_gatt::ScanFilter {
1241 rssi_high_threshold: 0,
1242 rssi_low_threshold: 0,
1243 rssi_low_timeout: 0,
1244 rssi_sampling_period: 0,
1245 condition: btstack::bluetooth_gatt::ScanFilterCondition::Patterns(vec![]),
1246 }),
1247 );
1248
1249 self.lock_context().active_scanner_ids.insert(scanner_id);
1250 }
1251 "stop-scan" => {
1252 let scanner_id = String::from(get_arg(args, 1)?)
1253 .parse::<u8>()
1254 .or(Err("Failed parsing scanner id"))?;
1255
1256 self.lock_context().gatt_dbus.as_mut().unwrap().stop_scan(scanner_id);
1257 self.lock_context().active_scanner_ids.remove(&scanner_id);
1258 }
1259 _ => return Err(CommandError::InvalidArgs),
1260 }
1261
1262 Ok(())
1263 }
1264
1265 // TODO(b/233128828): More options will be implemented to test BLE advertising.
1266 // Such as setting advertising parameters, starting multiple advertising sets, etc.
cmd_advertise(&mut self, args: &Vec<String>) -> CommandResult1267 fn cmd_advertise(&mut self, args: &Vec<String>) -> CommandResult {
1268 if !self.lock_context().adapter_ready {
1269 return Err(self.adapter_not_ready());
1270 }
1271
1272 if self.lock_context().advertiser_callback_id == None {
1273 return Err("No advertiser callback registered".into());
1274 }
1275
1276 let callback_id = self.lock_context().advertiser_callback_id.clone().unwrap();
1277
1278 let command = get_arg(args, 0)?;
1279
1280 match &command[..] {
1281 "on" => {
1282 print_info!("Creating legacy advertising set...");
1283 let s = AdvSet::new(true); // legacy advertising
1284 AdvSet::start(self.context.clone(), s, callback_id);
1285 }
1286 "off" => {
1287 AdvSet::stop_all(self.context.clone());
1288 }
1289 "ext" => {
1290 print_info!("Creating extended advertising set...");
1291 let s = AdvSet::new(false); // extended advertising
1292 AdvSet::start(self.context.clone(), s, callback_id);
1293 }
1294 "set-interval" => {
1295 let ms = String::from(get_arg(args, 1)?).parse::<i32>();
1296 if !ms.is_ok() {
1297 return Err("Failed parsing interval".into());
1298 }
1299 let interval = ms.unwrap() * 8 / 5; // in 0.625 ms.
1300
1301 let mut context = self.lock_context();
1302 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.interval = interval);
1303
1304 // To avoid borrowing context as mutable from an immutable borrow.
1305 // Required information is collected in advance and then passed
1306 // to the D-Bus call which requires a mutable borrow.
1307 let advs: Vec<(_, _)> = context
1308 .adv_sets
1309 .iter()
1310 .filter_map(|(_, s)| s.adv_id.map(|adv_id| (adv_id.clone(), s.params.clone())))
1311 .collect();
1312 for (adv_id, params) in advs {
1313 print_info!("Setting advertising parameters for {}", adv_id);
1314 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1315 }
1316 }
1317 "set-connectable" => {
1318 let connectable = match &get_arg(args, 1)?[..] {
1319 "on" => true,
1320 "off" => false,
1321 _ => false,
1322 };
1323
1324 let adv_id = String::from(get_arg(args, 2)?)
1325 .parse::<i32>()
1326 .or(Err("Failed parsing adv_id"))?;
1327
1328 let mut context = self.context.lock().unwrap();
1329
1330 let advs: Vec<(_, _)> = context
1331 .adv_sets
1332 .iter_mut()
1333 .filter_map(|(_, s)| {
1334 if !(s.adv_id.map_or(false, |id| id == adv_id)) {
1335 return None;
1336 }
1337 s.params.connectable = connectable;
1338 Some((s.params.clone(), s.data.clone()))
1339 })
1340 .collect();
1341
1342 for (params, data) in advs {
1343 print_info!("Setting advertising parameters for {}", adv_id);
1344 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1345
1346 // renew the flags
1347 print_info!("Setting advertising data for {}", adv_id);
1348 context.gatt_dbus.as_mut().unwrap().set_advertising_data(adv_id, data);
1349 }
1350 }
1351 "set-scan-rsp" => {
1352 let enable = match &get_arg(args, 1)?[..] {
1353 "enable" => true,
1354 "disable" => false,
1355 _ => false,
1356 };
1357
1358 let mut context = self.lock_context();
1359 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.scannable = enable);
1360
1361 let advs: Vec<(_, _, _)> = context
1362 .adv_sets
1363 .iter()
1364 .filter_map(|(_, s)| {
1365 s.adv_id
1366 .map(|adv_id| (adv_id.clone(), s.params.clone(), s.scan_rsp.clone()))
1367 })
1368 .collect();
1369 for (adv_id, params, scan_rsp) in advs {
1370 print_info!("Setting scan response data for {}", adv_id);
1371 context.gatt_dbus.as_mut().unwrap().set_scan_response_data(adv_id, scan_rsp);
1372 print_info!("Setting parameters for {}", adv_id);
1373 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1374 }
1375 }
1376 "set-raw-data" => {
1377 let data = hex::decode(get_arg(args, 1)?).or(Err("Failed parsing data"))?;
1378
1379 let adv_id = String::from(get_arg(args, 2)?)
1380 .parse::<i32>()
1381 .or(Err("Failed parsing adv_id"))?;
1382
1383 let mut context = self.context.lock().unwrap();
1384 if context
1385 .adv_sets
1386 .iter()
1387 .find(|(_, s)| s.adv_id.map_or(false, |id| id == adv_id))
1388 .is_none()
1389 {
1390 return Err("Failed to find advertising set".into());
1391 }
1392
1393 print_info!("Setting advertising data for {}", adv_id);
1394 context.gatt_dbus.as_mut().unwrap().set_raw_adv_data(adv_id, data);
1395 }
1396 _ => return Err(CommandError::InvalidArgs),
1397 }
1398
1399 Ok(())
1400 }
1401
cmd_sdp(&mut self, args: &Vec<String>) -> CommandResult1402 fn cmd_sdp(&mut self, args: &Vec<String>) -> CommandResult {
1403 if !self.lock_context().adapter_ready {
1404 return Err(self.adapter_not_ready());
1405 }
1406
1407 let command = get_arg(args, 0)?;
1408
1409 match &command[..] {
1410 "search" => {
1411 let device = BluetoothDevice {
1412 address: String::from(get_arg(args, 1)?),
1413 name: String::from(""),
1414 };
1415 let uuid = match UuidHelper::parse_string(get_arg(args, 2)?) {
1416 Some(uu) => uu.uu,
1417 None => return Err(CommandError::Failed("Invalid UUID".into())),
1418 };
1419 let success =
1420 self.lock_context().adapter_dbus.as_ref().unwrap().sdp_search(device, uuid);
1421 if !success {
1422 return Err("Unable to execute SDP search".into());
1423 }
1424 }
1425 _ => return Err(CommandError::InvalidArgs),
1426 }
1427 Ok(())
1428 }
1429
cmd_socket(&mut self, args: &Vec<String>) -> CommandResult1430 fn cmd_socket(&mut self, args: &Vec<String>) -> CommandResult {
1431 if !self.lock_context().adapter_ready {
1432 return Err(self.adapter_not_ready());
1433 }
1434
1435 let callback_id = match self.lock_context().socket_manager_callback_id.clone() {
1436 Some(id) => id,
1437 None => {
1438 return Err("No socket manager callback registered.".into());
1439 }
1440 };
1441
1442 let command = get_arg(args, 0)?;
1443
1444 match &command[..] {
1445 "set-on-connect-schedule" => {
1446 let schedule = match &get_arg(args, 1)?[..] {
1447 "send" => SocketSchedule {
1448 num_frame: 1,
1449 send_interval: Duration::from_millis(0),
1450 disconnect_delay: Duration::from_secs(30),
1451 },
1452 "resend" => SocketSchedule {
1453 num_frame: 3,
1454 send_interval: Duration::from_millis(100),
1455 disconnect_delay: Duration::from_secs(30),
1456 },
1457 "dump" => SocketSchedule {
1458 num_frame: 0,
1459 send_interval: Duration::from_millis(0),
1460 disconnect_delay: Duration::from_secs(30),
1461 },
1462 _ => {
1463 return Err("Failed to parse schedule".into());
1464 }
1465 };
1466
1467 self.context.lock().unwrap().socket_test_schedule = Some(schedule);
1468 }
1469 "send-msc" => {
1470 let dlci =
1471 String::from(get_arg(args, 1)?).parse::<u8>().or(Err("Failed parsing DLCI"))?;
1472 let addr = String::from(get_arg(args, 2)?);
1473 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().rfcomm_send_msc(dlci, addr);
1474 }
1475 "listen-rfcomm" => {
1476 let scn = String::from(get_arg(args, 1)?)
1477 .parse::<i32>()
1478 .or(Err("Failed parsing Service Channel Number"))?;
1479 let SocketResult { status, id } = self
1480 .context
1481 .lock()
1482 .unwrap()
1483 .socket_manager_dbus
1484 .as_mut()
1485 .unwrap()
1486 .listen_using_rfcomm(callback_id, Some(scn), None, None, None);
1487 if status != BtStatus::Success {
1488 return Err(format!(
1489 "Failed to request for listening using rfcomm, status = {:?}",
1490 status,
1491 )
1492 .into());
1493 }
1494 print_info!("Requested for listening using rfcomm on socket {}", id);
1495 }
1496 "listen" => {
1497 let auth_required = String::from(get_arg(args, 1)?)
1498 .parse::<bool>()
1499 .or(Err("Failed to parse auth-required"))?;
1500 let is_le = match &get_arg(args, 2)?[..] {
1501 "LE" => true,
1502 "Bredr" => false,
1503 _ => {
1504 return Err("Failed to parse socket type".into());
1505 }
1506 };
1507
1508 let SocketResult { status, id } = {
1509 let mut context_proxy = self.context.lock().unwrap();
1510 let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1511 if auth_required {
1512 if is_le {
1513 proxy.listen_using_l2cap_le_channel(callback_id)
1514 } else {
1515 proxy.listen_using_l2cap_channel(callback_id)
1516 }
1517 } else {
1518 if is_le {
1519 proxy.listen_using_insecure_l2cap_le_channel(callback_id)
1520 } else {
1521 proxy.listen_using_insecure_l2cap_channel(callback_id)
1522 }
1523 }
1524 };
1525
1526 if status != BtStatus::Success {
1527 return Err(format!(
1528 "Failed to request for listening using l2cap channel, status = {:?}",
1529 status,
1530 )
1531 .into());
1532 }
1533 print_info!("Requested for listening using l2cap channel on socket {}", id);
1534 }
1535 "connect" => {
1536 let (addr, sock_type, psm_or_uuid) =
1537 (&get_arg(args, 1)?, &get_arg(args, 2)?, &get_arg(args, 3)?);
1538 let device = BluetoothDevice {
1539 address: addr.clone().into(),
1540 name: String::from("Socket Connect Device"),
1541 };
1542
1543 let auth_required = String::from(get_arg(args, 4)?)
1544 .parse::<bool>()
1545 .or(Err("Failed to parse auth-required"))?;
1546
1547 let is_le = match &get_arg(args, 5)?[..] {
1548 "LE" => true,
1549 "Bredr" => false,
1550 _ => {
1551 return Err("Failed to parse socket type".into());
1552 }
1553 };
1554
1555 let SocketResult { status, id } = {
1556 let mut context_proxy = self.context.lock().unwrap();
1557 let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1558
1559 match &sock_type[0..] {
1560 "l2cap" => {
1561 let psm = match psm_or_uuid.clone().parse::<i32>() {
1562 Ok(v) => v,
1563 Err(e) => {
1564 return Err(CommandError::Failed(format!(
1565 "Bad PSM given. Error={}",
1566 e
1567 )));
1568 }
1569 };
1570
1571 if auth_required {
1572 if is_le {
1573 proxy.create_l2cap_le_channel(callback_id, device, psm)
1574 } else {
1575 proxy.create_l2cap_channel(callback_id, device, psm)
1576 }
1577 } else {
1578 if is_le {
1579 proxy.create_insecure_l2cap_le_channel(callback_id, device, psm)
1580 } else {
1581 proxy.create_insecure_l2cap_channel(callback_id, device, psm)
1582 }
1583 }
1584 }
1585 "rfcomm" => {
1586 let uuid = match UuidHelper::parse_string(psm_or_uuid.clone()) {
1587 Some(uu) => uu,
1588 None => {
1589 return Err(CommandError::Failed(format!(
1590 "Could not parse given uuid."
1591 )));
1592 }
1593 };
1594
1595 if auth_required {
1596 proxy.create_rfcomm_socket_to_service_record(
1597 callback_id,
1598 device,
1599 uuid,
1600 )
1601 } else {
1602 proxy.create_insecure_rfcomm_socket_to_service_record(
1603 callback_id,
1604 device,
1605 uuid,
1606 )
1607 }
1608 }
1609 _ => {
1610 return Err(CommandError::Failed(format!(
1611 "Unknown socket type: {}",
1612 sock_type
1613 )));
1614 }
1615 }
1616 };
1617
1618 if status != BtStatus::Success {
1619 return Err(CommandError::Failed(format!("Failed to create socket with status={:?} against {}, type {}, with psm/uuid {}",
1620 status, addr, sock_type, psm_or_uuid)));
1621 } else {
1622 print_info!("Called create socket with result ({:?}, {}) against {}, type {}, with psm/uuid {}",
1623 status, id, addr, sock_type, psm_or_uuid);
1624 }
1625 }
1626
1627 _ => return Err(CommandError::InvalidArgs),
1628 };
1629
1630 Ok(())
1631 }
1632
cmd_hid(&mut self, args: &Vec<String>) -> CommandResult1633 fn cmd_hid(&mut self, args: &Vec<String>) -> CommandResult {
1634 if !self.context.lock().unwrap().adapter_ready {
1635 return Err(self.adapter_not_ready());
1636 }
1637
1638 let command = get_arg(args, 0)?;
1639
1640 match &command[..] {
1641 "get-report" => {
1642 let addr = String::from(get_arg(args, 1)?);
1643 let report_type = match &get_arg(args, 2)?[..] {
1644 "Input" => BthhReportType::InputReport,
1645 "Output" => BthhReportType::OutputReport,
1646 "Feature" => BthhReportType::FeatureReport,
1647 _ => {
1648 return Err("Failed to parse report type".into());
1649 }
1650 };
1651 let report_id = String::from(get_arg(args, 3)?)
1652 .parse::<u8>()
1653 .or(Err("Failed parsing report_id"))?;
1654
1655 self.context.lock().unwrap().qa_legacy_dbus.as_mut().unwrap().get_hid_report(
1656 addr,
1657 report_type,
1658 report_id,
1659 );
1660 }
1661 "set-report" => {
1662 let addr = String::from(get_arg(args, 1)?);
1663 let report_type = match &get_arg(args, 2)?[..] {
1664 "Input" => BthhReportType::InputReport,
1665 "Output" => BthhReportType::OutputReport,
1666 "Feature" => BthhReportType::FeatureReport,
1667 _ => {
1668 return Err("Failed to parse report type".into());
1669 }
1670 };
1671 let report_value = String::from(get_arg(args, 3)?);
1672
1673 self.context.lock().unwrap().qa_legacy_dbus.as_mut().unwrap().set_hid_report(
1674 addr,
1675 report_type,
1676 report_value,
1677 );
1678 }
1679 "send-data" => {
1680 let addr = String::from(get_arg(args, 1)?);
1681 let data = String::from(get_arg(args, 2)?);
1682
1683 self.context
1684 .lock()
1685 .unwrap()
1686 .qa_legacy_dbus
1687 .as_mut()
1688 .unwrap()
1689 .send_hid_data(addr, data);
1690 }
1691 _ => return Err(CommandError::InvalidArgs),
1692 };
1693
1694 Ok(())
1695 }
1696
1697 /// Get the list of rules of supported commands
get_command_rule_list(&self) -> Vec<String>1698 pub fn get_command_rule_list(&self) -> Vec<String> {
1699 self.command_options.values().flat_map(|cmd| cmd.rules.clone()).collect()
1700 }
1701
cmd_list_devices(&mut self, args: &Vec<String>) -> CommandResult1702 fn cmd_list_devices(&mut self, args: &Vec<String>) -> CommandResult {
1703 if !self.lock_context().adapter_ready {
1704 return Err(self.adapter_not_ready());
1705 }
1706
1707 let command = get_arg(args, 0)?;
1708
1709 match &command[..] {
1710 "bonded" => {
1711 print_info!("Known bonded devices:");
1712 let devices =
1713 self.lock_context().adapter_dbus.as_ref().unwrap().get_bonded_devices();
1714 for device in devices.iter() {
1715 print_info!("[{:17}] {}", device.address, device.name);
1716 }
1717 }
1718 "found" => {
1719 print_info!("Devices found in most recent discovery session:");
1720 for (key, val) in self.lock_context().found_devices.iter() {
1721 print_info!("[{:17}] {}", key, val.name);
1722 }
1723 }
1724 "connected" => {
1725 print_info!("Connected devices:");
1726 let devices =
1727 self.lock_context().adapter_dbus.as_ref().unwrap().get_connected_devices();
1728 for device in devices.iter() {
1729 print_info!("[{:17}] {}", device.address, device.name);
1730 }
1731 }
1732 other => {
1733 println!("Invalid argument '{}'", other);
1734 }
1735 }
1736
1737 Ok(())
1738 }
1739
cmd_telephony(&mut self, args: &Vec<String>) -> CommandResult1740 fn cmd_telephony(&mut self, args: &Vec<String>) -> CommandResult {
1741 if !self.context.lock().unwrap().adapter_ready {
1742 return Err(self.adapter_not_ready());
1743 }
1744
1745 match &get_arg(args, 0)?[..] {
1746 "set-network" => {
1747 self.context
1748 .lock()
1749 .unwrap()
1750 .telephony_dbus
1751 .as_mut()
1752 .unwrap()
1753 .set_network_available(match &get_arg(args, 1)?[..] {
1754 "on" => true,
1755 "off" => false,
1756 other => {
1757 return Err(format!("Invalid argument '{}'", other).into());
1758 }
1759 });
1760 }
1761 "set-roaming" => {
1762 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().set_roaming(
1763 match &get_arg(args, 1)?[..] {
1764 "on" => true,
1765 "off" => false,
1766 other => {
1767 return Err(format!("Invalid argument '{}'", other).into());
1768 }
1769 },
1770 );
1771 }
1772 "set-signal" => {
1773 let strength = String::from(get_arg(args, 1)?)
1774 .parse::<i32>()
1775 .or(Err("Failed parsing signal strength"))?;
1776 if strength < 0 || strength > 5 {
1777 return Err(
1778 format!("Invalid signal strength, got {}, want 0 to 5", strength).into()
1779 );
1780 }
1781 self.context
1782 .lock()
1783 .unwrap()
1784 .telephony_dbus
1785 .as_mut()
1786 .unwrap()
1787 .set_signal_strength(strength);
1788 }
1789 "set-battery" => {
1790 let level = String::from(get_arg(args, 1)?)
1791 .parse::<i32>()
1792 .or(Err("Failed parsing battery level"))?;
1793 if level < 0 || level > 5 {
1794 return Err(format!("Invalid battery level, got {}, want 0 to 5", level).into());
1795 }
1796 self.context
1797 .lock()
1798 .unwrap()
1799 .telephony_dbus
1800 .as_mut()
1801 .unwrap()
1802 .set_battery_level(level);
1803 }
1804 "enable" => {
1805 let mut context = self.lock_context();
1806 context.telephony_dbus.as_mut().unwrap().set_phone_ops_enabled(true);
1807 if context.mps_sdp_handle.is_none() {
1808 let success = context
1809 .adapter_dbus
1810 .as_mut()
1811 .unwrap()
1812 .create_sdp_record(BtSdpRecord::Mps(BtSdpMpsRecord::default()));
1813 if !success {
1814 return Err(format!("Failed to create SDP record").into());
1815 }
1816 }
1817 }
1818 "disable" => {
1819 let mut context = self.lock_context();
1820 context.telephony_dbus.as_mut().unwrap().set_phone_ops_enabled(false);
1821 if let Some(handle) = context.mps_sdp_handle.take() {
1822 let success = context.adapter_dbus.as_mut().unwrap().remove_sdp_record(handle);
1823 if !success {
1824 return Err(format!("Failed to remove SDP record").into());
1825 }
1826 }
1827 }
1828 "incoming-call" => {
1829 let success = self
1830 .context
1831 .lock()
1832 .unwrap()
1833 .telephony_dbus
1834 .as_mut()
1835 .unwrap()
1836 .incoming_call(String::from(get_arg(args, 1)?));
1837 if !success {
1838 return Err("IncomingCall failed".into());
1839 }
1840 }
1841 "dialing-call" => {
1842 let success = self
1843 .context
1844 .lock()
1845 .unwrap()
1846 .telephony_dbus
1847 .as_mut()
1848 .unwrap()
1849 .dialing_call(String::from(get_arg(args, 1)?));
1850 if !success {
1851 return Err("DialingCall failed".into());
1852 }
1853 }
1854 "answer-call" => {
1855 let success =
1856 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().answer_call();
1857 if !success {
1858 return Err("AnswerCall failed".into());
1859 }
1860 }
1861 "hangup-call" => {
1862 let success =
1863 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().hangup_call();
1864 if !success {
1865 return Err("HangupCall failed".into());
1866 }
1867 }
1868 "set-memory-call" => {
1869 let success = self
1870 .context
1871 .lock()
1872 .unwrap()
1873 .telephony_dbus
1874 .as_mut()
1875 .unwrap()
1876 .set_memory_call(get_arg(args, 1).ok().map(String::from));
1877 if !success {
1878 return Err("SetMemoryCall failed".into());
1879 }
1880 }
1881 "set-last-call" => {
1882 let success = self
1883 .context
1884 .lock()
1885 .unwrap()
1886 .telephony_dbus
1887 .as_mut()
1888 .unwrap()
1889 .set_last_call(get_arg(args, 1).ok().map(String::from));
1890 if !success {
1891 return Err("SetLastCall failed".into());
1892 }
1893 }
1894 "release-held" => {
1895 let success =
1896 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().release_held();
1897 if !success {
1898 return Err("ReleaseHeld failed".into());
1899 }
1900 }
1901 "release-active-accept-held" => {
1902 let success = self
1903 .context
1904 .lock()
1905 .unwrap()
1906 .telephony_dbus
1907 .as_mut()
1908 .unwrap()
1909 .release_active_accept_held();
1910 if !success {
1911 return Err("ReleaseActiveAcceptHeld failed".into());
1912 }
1913 }
1914 "hold-active-accept-held" => {
1915 let success = self
1916 .context
1917 .lock()
1918 .unwrap()
1919 .telephony_dbus
1920 .as_mut()
1921 .unwrap()
1922 .hold_active_accept_held();
1923 if !success {
1924 return Err("HoldActiveAcceptHeld failed".into());
1925 }
1926 }
1927 "audio-connect" => {
1928 let success = self
1929 .context
1930 .lock()
1931 .unwrap()
1932 .telephony_dbus
1933 .as_mut()
1934 .unwrap()
1935 .audio_connect(String::from(get_arg(args, 1)?));
1936 if !success {
1937 return Err("ConnectAudio failed".into());
1938 }
1939 }
1940 "audio-disconnect" => {
1941 self.context
1942 .lock()
1943 .unwrap()
1944 .telephony_dbus
1945 .as_mut()
1946 .unwrap()
1947 .audio_disconnect(String::from(get_arg(args, 1)?));
1948 }
1949 other => {
1950 return Err(format!("Invalid argument '{}'", other).into());
1951 }
1952 }
1953 Ok(())
1954 }
1955
cmd_qa(&mut self, args: &Vec<String>) -> CommandResult1956 fn cmd_qa(&mut self, args: &Vec<String>) -> CommandResult {
1957 if !self.context.lock().unwrap().adapter_ready {
1958 return Err(self.adapter_not_ready());
1959 }
1960
1961 let command = get_arg(args, 0)?;
1962
1963 match &command[..] {
1964 "add-media-player" => {
1965 let name = String::from(get_arg(args, 1)?);
1966 let browsing_supported = String::from(get_arg(args, 2)?)
1967 .parse::<bool>()
1968 .or(Err("Failed to parse browsing_supported"))?;
1969 self.context
1970 .lock()
1971 .unwrap()
1972 .qa_dbus
1973 .as_mut()
1974 .unwrap()
1975 .add_media_player(name, browsing_supported);
1976 }
1977 _ => return Err(CommandError::InvalidArgs),
1978 };
1979
1980 Ok(())
1981 }
1982 }
1983
1984 #[cfg(test)]
1985 mod tests {
1986
1987 use super::*;
1988
1989 #[test]
test_wrap_help_text()1990 fn test_wrap_help_text() {
1991 let text = "hello";
1992 let text_len = text.chars().count();
1993 // ensure no overflow
1994 assert_eq!(format!("|{}|", text), wrap_help_text(text, 4, 0));
1995 assert_eq!(format!("|{}|", text), wrap_help_text(text, 5, 0));
1996 assert_eq!(format!("|{}{}|", text, " "), wrap_help_text(text, 6, 0));
1997 assert_eq!(format!("|{}{}|", text, " ".repeat(2)), wrap_help_text(text, 7, 0));
1998 assert_eq!(
1999 format!("|{}{}|", text, " ".repeat(100 - text_len)),
2000 wrap_help_text(text, 100, 0)
2001 );
2002 assert_eq!(format!("|{}{}|", " ", text), wrap_help_text(text, 4, 1));
2003 assert_eq!(format!("|{}{}|", " ".repeat(2), text), wrap_help_text(text, 5, 2));
2004 assert_eq!(format!("|{}{}{}|", " ".repeat(3), text, " "), wrap_help_text(text, 6, 3));
2005 assert_eq!(
2006 format!("|{}{}{}|", " ".repeat(4), text, " ".repeat(7 - text_len)),
2007 wrap_help_text(text, 7, 4)
2008 );
2009 assert_eq!(format!("|{}{}|", " ".repeat(9), text), wrap_help_text(text, 4, 9));
2010 assert_eq!(format!("|{}{}|", " ".repeat(10), text), wrap_help_text(text, 3, 10));
2011 assert_eq!(format!("|{}{}|", " ".repeat(11), text), wrap_help_text(text, 2, 11));
2012 assert_eq!(format!("|{}{}|", " ".repeat(12), text), wrap_help_text(text, 1, 12));
2013 assert_eq!("||", wrap_help_text("", 0, 0));
2014 assert_eq!("| |", wrap_help_text("", 1, 0));
2015 assert_eq!("| |", wrap_help_text("", 1, 1));
2016 assert_eq!("| |", wrap_help_text("", 0, 1));
2017 }
2018 }
2019