• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{
13     BtConnectionState, BtDiscMode, BtStatus, BtTransport, RawAddress, Uuid, INVALID_RSSI,
14 };
15 use bt_topshim::profiles::gatt::{GattStatus, LePhy};
16 use bt_topshim::profiles::hid_host::BthhReportType;
17 use bt_topshim::profiles::sdp::{BtSdpMpsRecord, BtSdpRecord};
18 use bt_topshim::profiles::ProfileConnectionState;
19 use bt_topshim::syslog::Level;
20 use btstack::battery_manager::IBatteryManager;
21 use btstack::bluetooth::{BluetoothDevice, IBluetooth};
22 use btstack::bluetooth_gatt::{
23     BluetoothGattCharacteristic, BluetoothGattDescriptor, BluetoothGattService, GattDbElementType,
24     GattWriteType, IBluetoothGatt,
25 };
26 use btstack::bluetooth_logging::IBluetoothLogging;
27 use btstack::bluetooth_media::{IBluetoothMedia, IBluetoothTelephony};
28 use btstack::bluetooth_qa::IBluetoothQA;
29 use btstack::socket_manager::{IBluetoothSocketManager, SocketResult};
30 use btstack::uuid::{Profile, UuidHelper};
31 use manager_service::iface_bluetooth_manager::IBluetoothManager;
32 
33 const INDENT_CHAR: &str = " ";
34 const BAR1_CHAR: &str = "=";
35 const BAR2_CHAR: &str = "-";
36 const MAX_MENU_CHAR_WIDTH: usize = 72;
37 
38 const GATT_CLIENT_APP_UUID: &str = "12345678123456781234567812345678";
39 const GATT_SERVER_APP_UUID: &str = "12345678123456781234567812345679";
40 const HEART_RATE_SERVICE_UUID: &str = "0000180D-0000-1000-8000-00805F9B34FB";
41 const HEART_RATE_MEASUREMENT_UUID: &str = "00002A37-0000-1000-8000-00805F9B34FB";
42 const GENERIC_UUID: &str = "00000000-0000-1000-8000-00805F9B34FB";
43 const CCC_DESCRIPTOR_UUID: &str = "00002902-0000-1000-8000-00805F9B34FB";
44 const BATTERY_SERVICE_UUID: &str = "0000180F-0000-1000-8000-00805F9B34FB";
45 
46 enum CommandError {
47     // Command not handled due to invalid arguments.
48     InvalidArgs,
49     // Command handled but failed with the given reason.
50     Failed(String),
51 }
52 
53 impl From<&str> for CommandError {
from(s: &str) -> CommandError54     fn from(s: &str) -> CommandError {
55         CommandError::Failed(String::from(s))
56     }
57 }
58 
59 impl From<String> for CommandError {
from(s: String) -> CommandError60     fn from(s: String) -> CommandError {
61         CommandError::Failed(s)
62     }
63 }
64 
65 type CommandResult = Result<(), CommandError>;
66 
67 type CommandFunction = fn(&mut CommandHandler, &[String]) -> CommandResult;
68 
_noop(_handler: &mut CommandHandler, _args: &[String]) -> CommandResult69 fn _noop(_handler: &mut CommandHandler, _args: &[String]) -> CommandResult {
70     // Used so we can add options with no direct function
71     // e.g. help and quit
72     Ok(())
73 }
74 
75 pub struct CommandOption {
76     rules: Vec<String>,
77     description: String,
78     function_pointer: CommandFunction,
79 }
80 
81 /// Handles string command entered from command line.
82 pub(crate) struct CommandHandler {
83     context: Arc<Mutex<ClientContext>>,
84     command_options: HashMap<String, CommandOption>,
85 }
86 
87 /// Define what to do when a socket connects. Mainly for qualification purposes.
88 /// Specifically, after a socket is connected/accepted, we will do
89 /// (1) send a chunk of data every |send_interval| time until |num_frame| chunks has been sent.
90 /// (2) wait another |disconnect_delay| time. any incoming data will be dumpted during this time.
91 /// (3) disconnect the socket.
92 #[derive(Copy, Clone)]
93 pub struct SocketSchedule {
94     /// Number of times to send data
95     pub num_frame: u32,
96     /// Time interval between each sending
97     pub send_interval: Duration,
98     /// Extra time after the last sending. Any incoming data will be printed during this time.
99     pub disconnect_delay: Duration,
100 }
101 
102 struct DisplayList<T>(Vec<T>);
103 
104 impl<T: Display> Display for DisplayList<T> {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result105     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106         let _ = writeln!(f, "[");
107         for item in self.0.iter() {
108             let _ = writeln!(f, "  {}", item);
109         }
110 
111         write!(f, "]")
112     }
113 }
114 
wrap_help_text(text: &str, max: usize, indent: usize) -> String115 fn wrap_help_text(text: &str, max: usize, indent: usize) -> String {
116     let remaining_count = std::cmp::max(
117         // real_max
118         std::cmp::max(max, text.chars().count())
119         // take away char count
120          - text.chars().count()
121         // take away real_indent
122          - (
123              if std::cmp::max(max, text.chars().count())- text.chars().count() > indent {
124                  indent
125              } else {
126                  0
127              }),
128         0,
129     );
130 
131     format!("|{}{}{}|", INDENT_CHAR.repeat(indent), text, INDENT_CHAR.repeat(remaining_count))
132 }
133 
134 // This should be called during the constructor in order to populate the command option map
build_commands() -> HashMap<String, CommandOption>135 fn build_commands() -> HashMap<String, CommandOption> {
136     let mut command_options = HashMap::<String, CommandOption>::new();
137     command_options.insert(
138         String::from("adapter"),
139         CommandOption {
140             rules: vec![
141                 String::from("adapter enable"),
142                 String::from("adapter disable"),
143                 String::from("adapter show"),
144                 String::from("adapter discoverable <on|limited|off> <duration>"),
145                 String::from("adapter connectable <on|off>"),
146                 String::from("adapter set-name <name>"),
147             ],
148             description: String::from(
149                 "Enable/Disable/Show default bluetooth adapter. (e.g. adapter enable)\n
150                  Discoverable On/Limited/Off (e.g. adapter discoverable on 60)\n
151                  Connectable On/Off (e.g. adapter connectable on)",
152             ),
153             function_pointer: CommandHandler::cmd_adapter,
154         },
155     );
156     command_options.insert(
157         String::from("battery"),
158         CommandOption {
159             rules: vec![
160                 String::from("battery status <address>"),
161                 String::from("battery track <address>"),
162                 String::from("battery untrack <address>"),
163             ],
164             description: String::from(
165                 "
166                 status: Current battery status of a given device.\n
167                 track: Track a given device to monitor battery updates.\n
168                 untrack: Stop tracking a device for battery updates.
169             ",
170             ),
171             function_pointer: CommandHandler::cmd_battery,
172         },
173     );
174     command_options.insert(
175         String::from("bond"),
176         CommandOption {
177             rules: vec![String::from("bond <add|remove|cancel> <address>")],
178             description: String::from("Creates a bond with a device."),
179             function_pointer: CommandHandler::cmd_bond,
180         },
181     );
182     command_options.insert(
183         String::from("device"),
184         CommandOption {
185             rules: vec![
186                 String::from("device <connect|disconnect|info> <address>"),
187                 String::from("device set-pairing-confirmation <address> <accept|reject>"),
188                 String::from("device set-pairing-pin <address> <pin|reject>"),
189                 String::from("device set-pairing-passkey <address> <passkey|reject>"),
190                 String::from("device set-alias <address> <new-alias>"),
191                 String::from("device get-rssi <address>"),
192             ],
193             description: String::from("Take action on a remote device. (i.e. info)"),
194             function_pointer: CommandHandler::cmd_device,
195         },
196     );
197     command_options.insert(
198         String::from("discovery"),
199         CommandOption {
200             rules: vec![String::from("discovery <start|stop>")],
201             description: String::from("Start and stop device discovery. (e.g. discovery start)"),
202             function_pointer: CommandHandler::cmd_discovery,
203         },
204     );
205     command_options.insert(
206         String::from("floss"),
207         CommandOption {
208             rules: vec![String::from("floss <enable|disable>")],
209             description: String::from("Enable or disable Floss for dogfood."),
210             function_pointer: CommandHandler::cmd_floss,
211         },
212     );
213     command_options.insert(
214         String::from("gatt"),
215         CommandOption {
216             rules: vec![
217                 String::from("gatt register-client"),
218                 String::from("gatt client-connect <address>"),
219                 String::from("gatt client-read-phy <address>"),
220                 String::from("gatt client-discover-services <address>"),
221                 String::from("gatt client-discover-service-by-uuid-pts <address> <uuid>"),
222                 String::from("gatt client-disconnect <address>"),
223                 String::from("gatt configure-mtu <address> <mtu>"),
224                 String::from("gatt set-direct-connect <true|false>"),
225                 String::from("gatt set-connect-transport <Bredr|LE|Auto>"),
226                 String::from("gatt set-connect-opportunistic <true|false>"),
227                 String::from("gatt set-connect-phy <Phy1m|Phy2m|PhyCoded>"),
228                 String::from("gatt set-auth-req <NONE|EncNoMitm|EncMitm|SignedNoMitm|SignedMitm>"),
229                 String::from(
230                     "gatt write-characteristic <address> <handle> <NoRsp|Write|Prepare> <value>",
231                 ),
232                 String::from("gatt read-characteristic <address> <handle>"),
233                 String::from(
234                     "gatt read-characteristic-by-uuid <address> <uuid> <start_handle> <end_handle>",
235                 ),
236                 String::from("gatt register-notification <address> <handle> <enable|disable>"),
237                 String::from("gatt register-server"),
238                 String::from("gatt unregister-server <server_id>"),
239                 String::from("gatt server-connect <server_id> <client_address>"),
240                 String::from("gatt server-disconnect <server_id> <client_address>"),
241                 String::from("gatt server-add-basic-service <server_id>"),
242                 String::from("gatt server-add-service <server_id> <incl_service_instance_id>"),
243                 String::from("gatt server-remove-service <server_id> <service_handle>"),
244                 String::from("gatt server-clear-all-services <server_id>"),
245                 String::from("gatt server-send-response <server_id> <success|fail>"),
246                 String::from("gatt server-set-direct-connect <true|false>"),
247                 String::from("gatt server-set-connect-transport <Bredr|LE|Auto>"),
248             ],
249             description: String::from(
250                 "GATT tools\n\n
251                 Creating a GATT Server:\n
252                 Register a server, then add a basic (battery) service. After, a more complex\n
253                 (heartrate) service can be created with previously created services included.",
254             ),
255             function_pointer: CommandHandler::cmd_gatt,
256         },
257     );
258     command_options.insert(
259         String::from("le-scan"),
260         CommandOption {
261             rules: vec![
262                 String::from("le-scan register-scanner"),
263                 String::from("le-scan unregister-scanner <scanner-id>"),
264                 String::from("le-scan start-scan <scanner-id>"),
265                 String::from("le-scan stop-scan <scanner-id>"),
266             ],
267             description: String::from("LE scanning utilities."),
268             function_pointer: CommandHandler::cmd_le_scan,
269         },
270     );
271     command_options.insert(
272         String::from("advertise"),
273         CommandOption {
274             rules: vec![
275                 String::from("advertise <on|off|ext>"),
276                 String::from("advertise set-interval <ms>"),
277                 String::from("advertise set-scan-rsp <enable|disable>"),
278                 String::from("advertise set-raw-data <raw-adv-data> <adv-id>"),
279                 String::from("advertise set-connectable <on|off> <adv-id>"),
280             ],
281             description: String::from("Advertising utilities."),
282             function_pointer: CommandHandler::cmd_advertise,
283         },
284     );
285     command_options.insert(
286         String::from("sdp"),
287         CommandOption {
288             rules: vec![String::from("sdp search <address> <uuid>")],
289             description: String::from("Service Discovery Protocol utilities."),
290             function_pointer: CommandHandler::cmd_sdp,
291         },
292     );
293     command_options.insert(
294         String::from("socket"),
295         CommandOption {
296             rules: vec![
297                 String::from("socket listen <auth-required> <Bredr|LE>"),
298                 String::from("socket listen-rfcomm <scn>"),
299                 String::from("socket send-msc <dlci> <address>"),
300                 String::from(
301                     "socket connect <address> <l2cap|rfcomm> <psm|uuid> <auth-required> <Bredr|LE>",
302                 ),
303                 String::from("socket close <socket_id>"),
304                 String::from("socket set-on-connect-schedule <send|resend|dump>"),
305             ],
306             description: String::from("Socket manager utilities."),
307             function_pointer: CommandHandler::cmd_socket,
308         },
309     );
310     command_options.insert(
311         String::from("hid"),
312         CommandOption {
313             rules: vec![
314                 String::from("hid get-report <address> <Input|Output|Feature> <report_id>"),
315                 String::from("hid set-report <address> <Input|Output|Feature> <report_value>"),
316                 String::from("hid send-data <address> <data>"),
317                 String::from("hid virtual-unplug <address>"),
318             ],
319             description: String::from("Socket manager utilities."),
320             function_pointer: CommandHandler::cmd_hid,
321         },
322     );
323     command_options.insert(
324         String::from("get-address"),
325         CommandOption {
326             rules: vec![String::from("get-address")],
327             description: String::from("Gets the local device address."),
328             function_pointer: CommandHandler::cmd_get_address,
329         },
330     );
331     command_options.insert(
332         String::from("qa"),
333         CommandOption {
334             rules: vec![String::from("qa add-media-player <name> <browsing_supported>")],
335             description: String::from("Methods for testing purposes"),
336             function_pointer: CommandHandler::cmd_qa,
337         },
338     );
339     command_options.insert(
340         String::from("help"),
341         CommandOption {
342             rules: vec![String::from("help")],
343             description: String::from("Shows this menu."),
344             function_pointer: CommandHandler::cmd_help,
345         },
346     );
347     command_options.insert(
348         String::from("list"),
349         CommandOption {
350             rules: vec![String::from("list <bonded|found|connected>")],
351             description: String::from(
352                 "List bonded or found remote devices. Use: list <bonded|found>",
353             ),
354             function_pointer: CommandHandler::cmd_list_devices,
355         },
356     );
357     command_options.insert(
358         String::from("telephony"),
359         CommandOption {
360             rules: vec![
361                 String::from("telephony set-network <on|off>"),
362                 String::from("telephony set-roaming <on|off>"),
363                 String::from("telephony set-signal <strength>"),
364                 String::from("telephony set-battery <level>"),
365                 String::from("telephony set-phone-opss <on|off>"),
366                 String::from("telephony <enable|disable>"),
367                 String::from("telephony <incoming-call|dialing-call> <number>"),
368                 String::from("telephony <answer-call|hangup-call>"),
369                 String::from("telephony <set-memory-call|set-last-call> [<number>]"),
370                 String::from(
371                     "telephony <release-held|release-active-accept-held|hold-active-accept-held>",
372                 ),
373                 String::from("telephony <audio-connect|audio-disconnect> <address>"),
374             ],
375             description: String::from("Set device telephony status."),
376             function_pointer: CommandHandler::cmd_telephony,
377         },
378     );
379     command_options.insert(
380         String::from("media"),
381         CommandOption {
382             rules: vec![String::from("media log")],
383             description: String::from("Audio tools."),
384             function_pointer: CommandHandler::cmd_media,
385         },
386     );
387     command_options.insert(
388         String::from("quit"),
389         CommandOption {
390             rules: vec![String::from("quit")],
391             description: String::from("Quit out of the interactive shell."),
392             function_pointer: _noop,
393         },
394     );
395     command_options.insert(
396         String::from("dumpsys"),
397         CommandOption {
398             rules: vec![String::from("dumpsys")],
399             description: String::from("Get diagnostic output."),
400             function_pointer: CommandHandler::cmd_dumpsys,
401         },
402     );
403     command_options.insert(
404         String::from("log"),
405         CommandOption {
406             rules: vec![
407                 String::from("log set-level <info|debug|verbose>"),
408                 String::from("log get-level"),
409             ],
410             description: String::from("Get/set log level"),
411             function_pointer: CommandHandler::cmd_log,
412         },
413     );
414     command_options
415 }
416 
417 // Helper to index a vector safely. The same as `args.get(i)` but converts the None into a
418 // CommandError::InvalidArgs.
419 //
420 // Use this to safely index an argument and conveniently return the error if the argument does not
421 // exist.
get_arg<I>( args: &[String], index: I, ) -> Result<&<I as SliceIndex<[String]>>::Output, CommandError> where I: SliceIndex<[String]>,422 fn get_arg<I>(
423     args: &[String],
424     index: I,
425 ) -> Result<&<I as SliceIndex<[String]>>::Output, CommandError>
426 where
427     I: SliceIndex<[String]>,
428 {
429     args.get(index).ok_or(CommandError::InvalidArgs)
430 }
431 
432 impl CommandHandler {
433     /// Creates a new CommandHandler.
new(context: Arc<Mutex<ClientContext>>) -> CommandHandler434     pub fn new(context: Arc<Mutex<ClientContext>>) -> CommandHandler {
435         CommandHandler { context, command_options: build_commands() }
436     }
437 
438     /// Entry point for command and arguments
process_cmd_line(&mut self, command: &str, args: &[String]) -> bool439     pub fn process_cmd_line(&mut self, command: &str, args: &[String]) -> bool {
440         // Ignore empty line
441         match command {
442             "" => false,
443             _ => match self.command_options.get(command) {
444                 Some(cmd) => {
445                     let rules = cmd.rules.clone();
446                     match (cmd.function_pointer)(self, args) {
447                         Ok(()) => true,
448                         Err(CommandError::InvalidArgs) => {
449                             print_error!("Invalid arguments. Usage:\n{}", rules.join("\n"));
450                             false
451                         }
452                         Err(CommandError::Failed(msg)) => {
453                             print_error!("Command failed: {}", msg);
454                             false
455                         }
456                     }
457                 }
458                 None => {
459                     println!("'{}' is an invalid command!", command);
460                     self.cmd_help(args).ok();
461                     false
462                 }
463             },
464         }
465     }
466 
lock_context(&self) -> std::sync::MutexGuard<ClientContext>467     fn lock_context(&self) -> std::sync::MutexGuard<ClientContext> {
468         self.context.lock().unwrap()
469     }
470 
471     // Common message for when the adapter isn't ready
adapter_not_ready(&self) -> CommandError472     fn adapter_not_ready(&self) -> CommandError {
473         format!(
474             "Default adapter {} is not enabled. Enable the adapter before using this command.",
475             self.lock_context().default_adapter
476         )
477         .into()
478     }
479 
cmd_help(&mut self, args: &[String]) -> CommandResult480     fn cmd_help(&mut self, args: &[String]) -> CommandResult {
481         if let Some(command) = args.first() {
482             match self.command_options.get(command) {
483                 Some(cmd) => {
484                     println!(
485                         "\n{}{}\n{}{}\n",
486                         INDENT_CHAR.repeat(4),
487                         command,
488                         INDENT_CHAR.repeat(8),
489                         cmd.description
490                     );
491                 }
492                 None => {
493                     println!("'{}' is an invalid command!", command);
494                     self.cmd_help(&[]).ok();
495                 }
496             }
497         } else {
498             // Build equals bar and Shave off sides
499             let equal_bar = format!(" {} ", BAR1_CHAR.repeat(MAX_MENU_CHAR_WIDTH));
500 
501             // Build empty bar and Shave off sides
502             let empty_bar = format!("|{}|", INDENT_CHAR.repeat(MAX_MENU_CHAR_WIDTH));
503 
504             // Header
505             println!(
506                 "\n{}\n{}\n+{}+\n{}",
507                 equal_bar,
508                 wrap_help_text("Help Menu", MAX_MENU_CHAR_WIDTH, 2),
509                 // Minus bar
510                 BAR2_CHAR.repeat(MAX_MENU_CHAR_WIDTH),
511                 empty_bar
512             );
513 
514             // Print commands
515             for (key, val) in self.command_options.iter() {
516                 println!(
517                     "{}\n{}\n{}",
518                     wrap_help_text(key, MAX_MENU_CHAR_WIDTH, 4),
519                     wrap_help_text(&val.description, MAX_MENU_CHAR_WIDTH, 8),
520                     empty_bar
521                 );
522             }
523 
524             // Footer
525             println!("{}\n{}", empty_bar, equal_bar);
526         }
527 
528         Ok(())
529     }
530 
cmd_adapter(&mut self, args: &[String]) -> CommandResult531     fn cmd_adapter(&mut self, args: &[String]) -> CommandResult {
532         if !self.lock_context().manager_dbus.get_floss_enabled() {
533             return Err("Floss is not enabled. First run, `floss enable`".into());
534         }
535 
536         let default_adapter = self.lock_context().default_adapter;
537 
538         let command = get_arg(args, 0)?;
539 
540         if matches!(&command[..], "show" | "discoverable" | "connectable" | "set-name") {
541             if !self.lock_context().adapter_ready {
542                 return Err(self.adapter_not_ready());
543             }
544         }
545 
546         match &command[..] {
547             "enable" => {
548                 if self.lock_context().is_restricted {
549                     return Err("You are not allowed to toggle adapter power".into());
550                 }
551                 self.lock_context().manager_dbus.start(default_adapter);
552             }
553             "disable" => {
554                 if self.lock_context().is_restricted {
555                     return Err("You are not allowed to toggle adapter power".into());
556                 }
557                 self.lock_context().manager_dbus.stop(default_adapter);
558             }
559             "show" => {
560                 let enabled = self.lock_context().enabled;
561                 let address = self.lock_context().adapter_address.unwrap_or_default();
562                 let context = self.lock_context();
563                 let adapter_dbus = context.adapter_dbus.as_ref().unwrap();
564                 let qa_dbus = context.qa_dbus.as_ref().unwrap();
565                 let name = adapter_dbus.get_name();
566                 let modalias = qa_dbus.get_modalias();
567                 let uuids = adapter_dbus.get_uuids();
568                 let is_discoverable = adapter_dbus.get_discoverable();
569                 let discoverable_timeout = adapter_dbus.get_discoverable_timeout();
570                 let cod = adapter_dbus.get_bluetooth_class();
571                 let multi_adv_supported = adapter_dbus.is_multi_advertisement_supported();
572                 let le_ext_adv_supported = adapter_dbus.is_le_extended_advertising_supported();
573                 let wbs_supported = adapter_dbus.is_wbs_supported();
574                 let le_audio_supported = adapter_dbus.is_le_audio_supported();
575                 let supported_profiles = UuidHelper::get_supported_profiles();
576                 let connected_profiles: Vec<(Profile, ProfileConnectionState)> = supported_profiles
577                     .iter()
578                     .map(|&prof| {
579                         if let Some(&uuid) = UuidHelper::get_profile_uuid(&prof) {
580                             (prof, adapter_dbus.get_profile_connection_state(uuid))
581                         } else {
582                             (prof, ProfileConnectionState::Disconnected)
583                         }
584                     })
585                     .filter(|(_prof, state)| state != &ProfileConnectionState::Disconnected)
586                     .collect();
587                 qa_dbus.fetch_connectable();
588                 qa_dbus.fetch_alias();
589                 qa_dbus.fetch_discoverable_mode();
590                 print_info!("Address: {}", address.to_string());
591                 print_info!("Name: {}", name);
592                 print_info!("Modalias: {}", modalias);
593                 print_info!("State: {}", if enabled { "enabled" } else { "disabled" });
594                 print_info!("Discoverable: {}", is_discoverable);
595                 print_info!("DiscoverableTimeout: {}s", discoverable_timeout);
596                 print_info!("Class: {:#06x}", cod);
597                 print_info!("IsMultiAdvertisementSupported: {}", multi_adv_supported);
598                 print_info!("IsLeExtendedAdvertisingSupported: {}", le_ext_adv_supported);
599                 print_info!("Connected profiles: {:?}", connected_profiles);
600                 print_info!("IsWbsSupported: {}", wbs_supported);
601                 print_info!("IsLeAudioSupported: {}", le_audio_supported);
602                 print_info!(
603                     "Uuids: {}",
604                     DisplayList(
605                         uuids
606                             .iter()
607                             .map(|&x| UuidHelper::known_uuid_to_string(&x))
608                             .collect::<Vec<String>>()
609                     )
610                 );
611             }
612             "discoverable" => match &get_arg(args, 1)?[..] {
613                 "on" => {
614                     let duration = String::from(get_arg(args, 2)?)
615                         .parse::<u32>()
616                         .or(Err("Failed parsing duration."))?;
617 
618                     let discoverable = self
619                         .lock_context()
620                         .adapter_dbus
621                         .as_mut()
622                         .unwrap()
623                         .set_discoverable(BtDiscMode::GeneralDiscoverable, duration);
624                     print_info!(
625                         "Set discoverable for {} seconds: {}",
626                         duration,
627                         if discoverable { "succeeded" } else { "failed" }
628                     );
629                 }
630                 "limited" => {
631                     let duration = String::from(get_arg(args, 2)?)
632                         .parse::<u32>()
633                         .or(Err("Failed parsing duration."))?;
634 
635                     let discoverable = self
636                         .lock_context()
637                         .adapter_dbus
638                         .as_mut()
639                         .unwrap()
640                         .set_discoverable(BtDiscMode::LimitedDiscoverable, duration);
641                     print_info!(
642                         "Set limited discoverable for {} seconds: {}",
643                         duration,
644                         if discoverable { "succeeded" } else { "failed" }
645                     );
646                 }
647                 "off" => {
648                     let discoverable = self
649                         .lock_context()
650                         .adapter_dbus
651                         .as_mut()
652                         .unwrap()
653                         .set_discoverable(BtDiscMode::NonDiscoverable, 0 /*not used*/);
654                     print_info!(
655                         "Turn discoverable off: {}",
656                         if discoverable { "succeeded" } else { "failed" }
657                     );
658                 }
659                 other => println!("Invalid argument for adapter discoverable '{}'", other),
660             },
661             "connectable" => match &get_arg(args, 1)?[..] {
662                 "on" => {
663                     self.lock_context().qa_dbus.as_mut().unwrap().set_connectable(true);
664                 }
665                 "off" => {
666                     self.lock_context().qa_dbus.as_mut().unwrap().set_connectable(false);
667                 }
668                 other => println!("Invalid argument for adapter connectable '{}'", other),
669             },
670             "set-name" => {
671                 if let Some(name) = args.get(1) {
672                     self.lock_context().adapter_dbus.as_ref().unwrap().set_name(name.to_string());
673                 } else {
674                     println!("usage: adapter set-name <name>");
675                 }
676             }
677 
678             _ => return Err(CommandError::InvalidArgs),
679         };
680 
681         Ok(())
682     }
683 
cmd_get_address(&mut self, _args: &[String]) -> CommandResult684     fn cmd_get_address(&mut self, _args: &[String]) -> CommandResult {
685         if !self.lock_context().adapter_ready {
686             return Err(self.adapter_not_ready());
687         }
688 
689         let address = self.lock_context().update_adapter_address();
690         print_info!("Local address = {}", address.to_string());
691         Ok(())
692     }
693 
cmd_discovery(&mut self, args: &[String]) -> CommandResult694     fn cmd_discovery(&mut self, args: &[String]) -> CommandResult {
695         if !self.lock_context().adapter_ready {
696             return Err(self.adapter_not_ready());
697         }
698 
699         let command = get_arg(args, 0)?;
700 
701         match &command[..] {
702             "start" => {
703                 self.lock_context().adapter_dbus.as_mut().unwrap().start_discovery();
704             }
705             "stop" => {
706                 self.lock_context().adapter_dbus.as_mut().unwrap().cancel_discovery();
707             }
708             _ => return Err(CommandError::InvalidArgs),
709         }
710 
711         Ok(())
712     }
713 
cmd_battery(&mut self, args: &[String]) -> CommandResult714     fn cmd_battery(&mut self, args: &[String]) -> CommandResult {
715         if !self.lock_context().adapter_ready {
716             return Err(self.adapter_not_ready());
717         }
718 
719         let command = get_arg(args, 0)?;
720         let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
721         let address = addr.to_string();
722 
723         match &command[..] {
724             "status" => {
725                 match self
726                     .lock_context()
727                     .battery_manager_dbus
728                     .as_ref()
729                     .unwrap()
730                     .get_battery_information(addr)
731                 {
732                     None => println!("Battery status for device {} could not be fetched", address),
733                     Some(set) => {
734                         if set.batteries.is_empty() {
735                             println!("Battery set for device {} is empty", set.address.to_string());
736                             return Ok(());
737                         }
738 
739                         println!(
740                             "Battery data for '{}' from source '{}' and uuid '{}':",
741                             set.address.to_string(),
742                             set.source_uuid.clone(),
743                             set.source_info.clone()
744                         );
745                         for battery in set.batteries {
746                             println!("   {}%, variant: '{}'", battery.percentage, battery.variant);
747                         }
748                     }
749                 }
750             }
751             "track" => {
752                 if self.lock_context().battery_address_filter.contains(&address) {
753                     println!("Already tracking {}", address);
754                     return Ok(());
755                 }
756                 self.lock_context().battery_address_filter.insert(address);
757 
758                 println!("Currently tracking:");
759                 for addr in self.lock_context().battery_address_filter.iter() {
760                     println!("{}", addr);
761                 }
762             }
763             "untrack" => {
764                 if !self.lock_context().battery_address_filter.remove(&address) {
765                     println!("Not tracking {}", address);
766                     return Ok(());
767                 }
768                 println!("Stopped tracking {}", address);
769 
770                 if self.lock_context().battery_address_filter.is_empty() {
771                     println!("No longer tracking any addresses for battery status updates");
772                     return Ok(());
773                 }
774 
775                 println!("Currently tracking:");
776                 for addr in self.lock_context().battery_address_filter.iter() {
777                     println!("{}", addr);
778                 }
779             }
780             _ => return Err(CommandError::InvalidArgs),
781         }
782         Ok(())
783     }
784 
cmd_bond(&mut self, args: &[String]) -> CommandResult785     fn cmd_bond(&mut self, args: &[String]) -> CommandResult {
786         if !self.lock_context().adapter_ready {
787             return Err(self.adapter_not_ready());
788         }
789 
790         let command = get_arg(args, 0)?;
791 
792         match &command[..] {
793             "add" => {
794                 let device = BluetoothDevice {
795                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
796                     name: String::from("Classic Device"),
797                 };
798 
799                 let bonding_attempt = &self.lock_context().bonding_attempt.as_ref().cloned();
800 
801                 if bonding_attempt.is_some() {
802                     return Err(format!(
803                         "Already bonding [{}]. Cancel bonding first.",
804                         bonding_attempt.as_ref().unwrap().address.to_string(),
805                     )
806                     .into());
807                 }
808 
809                 let status = self
810                     .lock_context()
811                     .adapter_dbus
812                     .as_mut()
813                     .unwrap()
814                     .create_bond(device.clone(), BtTransport::Auto);
815 
816                 if status == BtStatus::Success {
817                     self.lock_context().bonding_attempt = Some(device);
818                 }
819             }
820             "remove" => {
821                 let device = BluetoothDevice {
822                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
823                     name: String::from("Classic Device"),
824                 };
825 
826                 self.lock_context().adapter_dbus.as_mut().unwrap().remove_bond(device);
827             }
828             "cancel" => {
829                 let device = BluetoothDevice {
830                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
831                     name: String::from("Classic Device"),
832                 };
833 
834                 self.lock_context().adapter_dbus.as_mut().unwrap().cancel_bond_process(device);
835             }
836             other => {
837                 println!("Invalid argument '{}'", other);
838             }
839         }
840 
841         Ok(())
842     }
843 
cmd_device(&mut self, args: &[String]) -> CommandResult844     fn cmd_device(&mut self, args: &[String]) -> CommandResult {
845         if !self.lock_context().adapter_ready {
846             return Err(self.adapter_not_ready());
847         }
848 
849         let command = &get_arg(args, 0)?;
850 
851         match &command[..] {
852             "connect" => {
853                 let device = BluetoothDevice {
854                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
855                     name: String::from("Classic Device"),
856                 };
857 
858                 let status = self
859                     .lock_context()
860                     .adapter_dbus
861                     .as_mut()
862                     .unwrap()
863                     .connect_all_enabled_profiles(device.clone());
864 
865                 if status == BtStatus::Success {
866                     println!("Connecting to {}", &device.address.to_string());
867                 } else {
868                     println!("Can't connect to {}", &device.address.to_string());
869                 }
870             }
871             "disconnect" => {
872                 let device = BluetoothDevice {
873                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
874                     name: String::from("Classic Device"),
875                 };
876 
877                 let success = self
878                     .lock_context()
879                     .adapter_dbus
880                     .as_mut()
881                     .unwrap()
882                     .disconnect_all_enabled_profiles(device.clone());
883 
884                 if success {
885                     println!("Disconnecting from {}", &device.address.to_string());
886                 } else {
887                     println!("Can't disconnect from {}", &device.address.to_string());
888                 }
889             }
890             "info" => {
891                 let device = BluetoothDevice {
892                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
893                     name: String::from("Classic Device"),
894                 };
895 
896                 let (
897                     name,
898                     alias,
899                     device_type,
900                     addr_type,
901                     class,
902                     appearance,
903                     modalias,
904                     bonded,
905                     connection_state,
906                     uuids,
907                     wake_allowed,
908                     dual_mode_audio,
909                 ) = {
910                     let ctx = self.lock_context();
911                     let adapter = ctx.adapter_dbus.as_ref().unwrap();
912 
913                     let name = adapter.get_remote_name(device.clone());
914                     let device_type = adapter.get_remote_type(device.clone());
915                     let addr_type = adapter.get_remote_address_type(device.clone());
916                     let alias = adapter.get_remote_alias(device.clone());
917                     let class = adapter.get_remote_class(device.clone());
918                     let appearance = adapter.get_remote_appearance(device.clone());
919                     let modalias =
920                         adapter.get_remote_vendor_product_info(device.clone()).to_string();
921                     let bonded = adapter.get_bond_state(device.clone());
922                     let connection_state = match adapter.get_connection_state(device.clone()) {
923                         BtConnectionState::NotConnected => "Not Connected",
924                         BtConnectionState::ConnectedOnly => "Connected",
925                         _ => "Connected and Paired",
926                     };
927                     let uuids = adapter.get_remote_uuids(device.clone());
928                     let wake_allowed = adapter.get_remote_wake_allowed(device.clone());
929                     let dual_mode_audio = adapter.is_dual_mode_audio_sink_device(device.clone());
930 
931                     (
932                         name,
933                         alias,
934                         device_type,
935                         addr_type,
936                         class,
937                         appearance,
938                         modalias,
939                         bonded,
940                         connection_state,
941                         uuids,
942                         wake_allowed,
943                         dual_mode_audio,
944                     )
945                 };
946 
947                 print_info!("Address: {}", &device.address.to_string());
948                 print_info!("Name: {}", name);
949                 print_info!("Alias: {}", alias);
950                 print_info!("Device Type: {:?}", device_type);
951                 print_info!("Address Type: {:?}", addr_type);
952                 print_info!("Class: {}", class);
953                 print_info!("Appearance: {}", appearance);
954                 print_info!("Modalias: {}", modalias);
955                 print_info!("Wake Allowed: {}", wake_allowed);
956                 print_info!("Bond State: {:?}", bonded);
957                 print_info!("Connection State: {}", connection_state);
958                 print_info!("Dual Mode Audio Device: {}", dual_mode_audio);
959                 print_info!(
960                     "Uuids: {}",
961                     DisplayList(
962                         uuids
963                             .iter()
964                             .map(|&x| UuidHelper::known_uuid_to_string(&x))
965                             .collect::<Vec<String>>()
966                     )
967                 );
968             }
969             "set-alias" => {
970                 let new_alias = get_arg(args, 2)?;
971                 let device = BluetoothDevice {
972                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
973                     name: String::from(""),
974                 };
975                 let old_alias = self
976                     .lock_context()
977                     .adapter_dbus
978                     .as_ref()
979                     .unwrap()
980                     .get_remote_alias(device.clone());
981                 println!(
982                     "Updating alias for {}: {} -> {}",
983                     get_arg(args, 1)?,
984                     old_alias,
985                     new_alias
986                 );
987                 self.lock_context()
988                     .adapter_dbus
989                     .as_mut()
990                     .unwrap()
991                     .set_remote_alias(device.clone(), new_alias.clone());
992             }
993             "set-pairing-confirmation" => {
994                 let device = BluetoothDevice {
995                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
996                     name: String::from(""),
997                 };
998                 let accept = match &get_arg(args, 2)?[..] {
999                     "accept" => true,
1000                     "reject" => false,
1001                     other => {
1002                         return Err(format!("Failed to parse '{}'", other).into());
1003                     }
1004                 };
1005 
1006                 self.lock_context()
1007                     .adapter_dbus
1008                     .as_mut()
1009                     .unwrap()
1010                     .set_pairing_confirmation(device.clone(), accept);
1011             }
1012             "set-pairing-pin" => {
1013                 let device = BluetoothDevice {
1014                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
1015                     name: String::from(""),
1016                 };
1017                 let pin = get_arg(args, 2)?;
1018 
1019                 let (accept, pin) = match (&pin[..], pin) {
1020                     ("reject", _) => (false, vec![]),
1021                     (_, p) => (true, p.as_bytes().to_vec()),
1022                 };
1023 
1024                 self.lock_context().adapter_dbus.as_mut().unwrap().set_pin(
1025                     device.clone(),
1026                     accept,
1027                     pin,
1028                 );
1029             }
1030             "set-pairing-passkey" => {
1031                 let device = BluetoothDevice {
1032                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
1033                     name: String::from(""),
1034                 };
1035                 let passkey = get_arg(args, 2)?;
1036                 let (accept, passkey) = match (&passkey[..], String::from(passkey).parse::<u32>()) {
1037                     (_, Ok(p)) => (true, Vec::from(p.to_ne_bytes())),
1038                     ("reject", _) => (false, vec![]),
1039                     _ => {
1040                         return Err(format!("Failed to parse '{}'", passkey).into());
1041                     }
1042                 };
1043 
1044                 self.lock_context().adapter_dbus.as_mut().unwrap().set_passkey(
1045                     device.clone(),
1046                     accept,
1047                     passkey,
1048                 );
1049             }
1050             "get-rssi" => {
1051                 let device = BluetoothDevice {
1052                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
1053                     name: String::from(""),
1054                 };
1055 
1056                 match self
1057                     .lock_context()
1058                     .adapter_dbus
1059                     .as_mut()
1060                     .unwrap()
1061                     .get_remote_rssi(device.clone())
1062                 {
1063                     INVALID_RSSI => {
1064                         println!("Invalid RSSI");
1065                     }
1066                     rssi => {
1067                         println!("RSSI: {}", rssi);
1068                     }
1069                 };
1070             }
1071             other => {
1072                 println!("Invalid argument '{}'", other);
1073             }
1074         }
1075 
1076         Ok(())
1077     }
1078 
cmd_floss(&mut self, args: &[String]) -> CommandResult1079     fn cmd_floss(&mut self, args: &[String]) -> CommandResult {
1080         let command = get_arg(args, 0)?;
1081 
1082         match &command[..] {
1083             "enable" => {
1084                 self.lock_context().manager_dbus.set_floss_enabled(true);
1085             }
1086             "disable" => {
1087                 self.lock_context().manager_dbus.set_floss_enabled(false);
1088             }
1089             "show" => {
1090                 let (major, minor) = self.lock_context().get_floss_api_version();
1091                 print_info!("Floss API version: {}.{}", major, minor);
1092                 print_info!(
1093                     "Floss enabled: {}",
1094                     self.lock_context().manager_dbus.get_floss_enabled()
1095                 );
1096             }
1097             _ => return Err(CommandError::InvalidArgs),
1098         }
1099 
1100         Ok(())
1101     }
1102 
cmd_gatt(&mut self, args: &[String]) -> CommandResult1103     fn cmd_gatt(&mut self, args: &[String]) -> CommandResult {
1104         if !self.lock_context().adapter_ready {
1105             return Err(self.adapter_not_ready());
1106         }
1107 
1108         let command = get_arg(args, 0)?;
1109 
1110         match &command[..] {
1111             "register-client" => {
1112                 let dbus_connection = self.lock_context().dbus_connection.clone();
1113                 let dbus_crossroads = self.lock_context().dbus_crossroads.clone();
1114 
1115                 self.lock_context().gatt_dbus.as_mut().unwrap().register_client(
1116                     String::from(GATT_CLIENT_APP_UUID),
1117                     Box::new(BtGattCallback::new(
1118                         String::from("/org/chromium/bluetooth/client/bluetooth_gatt_callback"),
1119                         self.context.clone(),
1120                         dbus_connection,
1121                         dbus_crossroads,
1122                     )),
1123                     false,
1124                 );
1125             }
1126             "client-connect" => {
1127                 let client_id = self
1128                     .lock_context()
1129                     .gatt_client_context
1130                     .client_id
1131                     .ok_or("GATT client is not yet registered.")?;
1132 
1133                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1134                 let is_direct = self.lock_context().gatt_client_context.is_connect_direct;
1135                 let transport = self.lock_context().gatt_client_context.connect_transport;
1136                 let oppurtunistic = self.lock_context().gatt_client_context.connect_opportunistic;
1137                 let phy = self.lock_context().gatt_client_context.connect_phy;
1138 
1139                 println!("Initiating GATT client connect. client_id: {}, addr: {}, is_direct: {}, transport: {:?}, oppurtunistic: {}, phy: {:?}", client_id, addr.to_string(), is_direct, transport, oppurtunistic, phy);
1140                 self.lock_context().gatt_dbus.as_ref().unwrap().client_connect(
1141                     client_id,
1142                     addr,
1143                     is_direct,
1144                     transport,
1145                     oppurtunistic,
1146                     phy,
1147                 );
1148             }
1149             "client-disconnect" => {
1150                 let client_id = self
1151                     .lock_context()
1152                     .gatt_client_context
1153                     .client_id
1154                     .ok_or("GATT client is not yet registered.")?;
1155 
1156                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1157                 self.lock_context().gatt_dbus.as_ref().unwrap().client_disconnect(client_id, addr);
1158             }
1159             "client-read-phy" => {
1160                 let client_id = self
1161                     .lock_context()
1162                     .gatt_client_context
1163                     .client_id
1164                     .ok_or("GATT client is not yet registered.")?;
1165                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1166                 self.lock_context().gatt_dbus.as_mut().unwrap().client_read_phy(client_id, addr);
1167             }
1168             "client-discover-services" => {
1169                 let client_id = self
1170                     .lock_context()
1171                     .gatt_client_context
1172                     .client_id
1173                     .ok_or("GATT client is not yet registered.")?;
1174 
1175                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1176                 self.lock_context().gatt_dbus.as_ref().unwrap().discover_services(client_id, addr);
1177             }
1178             "client-discover-service-by-uuid-pts" => {
1179                 let client_id = self
1180                     .lock_context()
1181                     .gatt_client_context
1182                     .client_id
1183                     .ok_or("GATT client is not yet registered.")?;
1184                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1185                 let uuid = String::from(get_arg(args, 2)?);
1186                 self.lock_context()
1187                     .gatt_dbus
1188                     .as_ref()
1189                     .unwrap()
1190                     .btif_gattc_discover_service_by_uuid(client_id, addr, uuid);
1191             }
1192             "configure-mtu" => {
1193                 let client_id = self
1194                     .lock_context()
1195                     .gatt_client_context
1196                     .client_id
1197                     .ok_or("GATT client is not yet registered.")?;
1198 
1199                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1200                 let mtu =
1201                     String::from(get_arg(args, 2)?).parse::<i32>().or(Err("Failed parsing mtu"))?;
1202 
1203                 self.lock_context().gatt_dbus.as_ref().unwrap().configure_mtu(client_id, addr, mtu)
1204             }
1205             "set-direct-connect" => {
1206                 let is_direct = String::from(get_arg(args, 1)?)
1207                     .parse::<bool>()
1208                     .or(Err("Failed to parse is_direct"))?;
1209 
1210                 self.lock_context().gatt_client_context.is_connect_direct = is_direct;
1211             }
1212             "set-connect-transport" => {
1213                 let transport = match &get_arg(args, 1)?[..] {
1214                     "Bredr" => BtTransport::Bredr,
1215                     "LE" => BtTransport::Le,
1216                     "Auto" => BtTransport::Auto,
1217                     _ => {
1218                         return Err("Failed to parse transport".into());
1219                     }
1220                 };
1221                 self.lock_context().gatt_client_context.connect_transport = transport;
1222             }
1223             "set-connect-opportunistic" => {
1224                 let opportunistic = String::from(get_arg(args, 1)?)
1225                     .parse::<bool>()
1226                     .or(Err("Failed to parse opportunistic"))?;
1227 
1228                 self.lock_context().gatt_client_context.connect_opportunistic = opportunistic;
1229             }
1230             "set-connect-phy" => {
1231                 let phy = match &get_arg(args, 1)?[..] {
1232                     "Phy1m" => LePhy::Phy1m,
1233                     "Phy2m" => LePhy::Phy2m,
1234                     "PhyCoded" => LePhy::PhyCoded,
1235                     _ => {
1236                         return Err("Failed to parse phy".into());
1237                     }
1238                 };
1239 
1240                 self.lock_context().gatt_client_context.connect_phy = phy;
1241             }
1242             "set-auth-req" => {
1243                 let flag = match &get_arg(args, 1)?[..] {
1244                     "NONE" => AuthReq::NoEnc,
1245                     "EncNoMitm" => AuthReq::EncNoMitm,
1246                     "EncMitm" => AuthReq::EncMitm,
1247                     "SignedNoMitm" => AuthReq::SignedNoMitm,
1248                     "SignedMitm" => AuthReq::SignedMitm,
1249                     _ => {
1250                         return Err("Failed to parse auth-req".into());
1251                     }
1252                 };
1253 
1254                 self.lock_context().gatt_client_context.auth_req = flag;
1255                 println!("AuthReq: {:?}", self.lock_context().gatt_client_context.get_auth_req());
1256             }
1257             "write-characteristic" => {
1258                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1259                 let handle = String::from(get_arg(args, 2)?)
1260                     .parse::<i32>()
1261                     .or(Err("Failed to parse handle"))?;
1262 
1263                 let write_type = match &get_arg(args, 3)?[..] {
1264                     "NoRsp" => GattWriteType::WriteNoRsp,
1265                     "Write" => GattWriteType::Write,
1266                     "Prepare" => GattWriteType::WritePrepare,
1267                     _ => {
1268                         return Err("Failed to parse write-type".into());
1269                     }
1270                 };
1271 
1272                 let value = hex::decode(get_arg(args, 4)?).or(Err("Failed to parse value"))?;
1273 
1274                 let client_id = self
1275                     .lock_context()
1276                     .gatt_client_context
1277                     .client_id
1278                     .ok_or("GATT client is not yet registered.")?;
1279 
1280                 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1281 
1282                 self.lock_context()
1283                     .gatt_dbus
1284                     .as_mut()
1285                     .unwrap()
1286                     .write_characteristic(client_id, addr, handle, write_type, auth_req, value);
1287             }
1288             "read-characteristic" => {
1289                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1290                 let handle = String::from(get_arg(args, 2)?)
1291                     .parse::<i32>()
1292                     .or(Err("Failed to parse handle"))?;
1293                 let client_id = self
1294                     .lock_context()
1295                     .gatt_client_context
1296                     .client_id
1297                     .ok_or("GATT client is not yet registered.")?;
1298 
1299                 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1300 
1301                 self.lock_context()
1302                     .gatt_dbus
1303                     .as_ref()
1304                     .unwrap()
1305                     .read_characteristic(client_id, addr, handle, auth_req);
1306             }
1307             "read-characteristic-by-uuid" => {
1308                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1309                 let uuid = String::from(get_arg(args, 2)?);
1310                 let start_handle = String::from(get_arg(args, 3)?)
1311                     .parse::<i32>()
1312                     .or(Err("Failed to parse start handle"))?;
1313                 let end_handle = String::from(get_arg(args, 4)?)
1314                     .parse::<i32>()
1315                     .or(Err("Failed to parse end handle"))?;
1316 
1317                 let client_id = self
1318                     .lock_context()
1319                     .gatt_client_context
1320                     .client_id
1321                     .ok_or("GATT client is not yet registered.")?;
1322 
1323                 let auth_req = self.lock_context().gatt_client_context.get_auth_req().into();
1324 
1325                 self.lock_context().gatt_dbus.as_ref().unwrap().read_using_characteristic_uuid(
1326                     client_id,
1327                     addr,
1328                     uuid,
1329                     start_handle,
1330                     end_handle,
1331                     auth_req,
1332                 );
1333             }
1334             "register-notification" => {
1335                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
1336                 let handle = String::from(get_arg(args, 2)?)
1337                     .parse::<i32>()
1338                     .or(Err("Failed to parse handle"))?;
1339                 let enable = match &get_arg(args, 3)?[..] {
1340                     "enable" => true,
1341                     "disable" => false,
1342                     _ => {
1343                         return Err("Failed to parse enable".into());
1344                     }
1345                 };
1346 
1347                 let client_id = self
1348                     .lock_context()
1349                     .gatt_client_context
1350                     .client_id
1351                     .ok_or("GATT client is not yet registered.")?;
1352 
1353                 self.lock_context()
1354                     .gatt_dbus
1355                     .as_ref()
1356                     .unwrap()
1357                     .register_for_notification(client_id, addr, handle, enable);
1358             }
1359             "register-server" => {
1360                 let dbus_connection = self.lock_context().dbus_connection.clone();
1361                 let dbus_crossroads = self.lock_context().dbus_crossroads.clone();
1362 
1363                 self.lock_context().gatt_dbus.as_mut().unwrap().register_server(
1364                     String::from(GATT_SERVER_APP_UUID),
1365                     Box::new(BtGattServerCallback::new(
1366                         String::from(
1367                             "/org/chromium/bluetooth/client/bluetooth_gatt_server_callback",
1368                         ),
1369                         self.context.clone(),
1370                         dbus_connection,
1371                         dbus_crossroads,
1372                     )),
1373                     false,
1374                 );
1375             }
1376             "unregister-server" => {
1377                 let server_id = String::from(get_arg(args, 1)?)
1378                     .parse::<i32>()
1379                     .or(Err("Failed parsing server id"))?;
1380 
1381                 self.lock_context().gatt_dbus.as_mut().unwrap().unregister_server(server_id);
1382             }
1383             "server-connect" => {
1384                 let server_id = String::from(get_arg(args, 1)?)
1385                     .parse::<i32>()
1386                     .or(Err("Failed to parse server_id"))?;
1387                 let client_addr =
1388                     RawAddress::from_string(get_arg(args, 2)?).ok_or("Invalid Address")?;
1389                 let is_direct = self.lock_context().gatt_server_context.is_connect_direct;
1390                 let transport = self.lock_context().gatt_server_context.connect_transport;
1391 
1392                 if !self.lock_context().gatt_dbus.as_mut().unwrap().server_connect(
1393                     server_id,
1394                     client_addr,
1395                     is_direct,
1396                     transport,
1397                 ) {
1398                     return Err("Connection was unsuccessful".into());
1399                 }
1400             }
1401             "server-disconnect" => {
1402                 let server_id = String::from(get_arg(args, 1)?)
1403                     .parse::<i32>()
1404                     .or(Err("Failed to parse server_id"))?;
1405                 let client_addr =
1406                     RawAddress::from_string(get_arg(args, 2)?).ok_or("Invalid Address")?;
1407 
1408                 if !self
1409                     .lock_context()
1410                     .gatt_dbus
1411                     .as_mut()
1412                     .unwrap()
1413                     .server_disconnect(server_id, client_addr)
1414                 {
1415                     return Err("Disconnection was unsuccessful".into());
1416                 }
1417             }
1418             "server-add-basic-service" => {
1419                 let service_uuid = Uuid::from_string(BATTERY_SERVICE_UUID).unwrap();
1420 
1421                 let server_id = String::from(get_arg(args, 1)?)
1422                     .parse::<i32>()
1423                     .or(Err("Failed to parse server_id"))?;
1424 
1425                 let service = BluetoothGattService::new(
1426                     service_uuid,
1427                     0, // libbluetooth assigns this handle once the service is added
1428                     GattDbElementType::PrimaryService.into(),
1429                 );
1430 
1431                 self.lock_context().gatt_dbus.as_mut().unwrap().add_service(server_id, service);
1432             }
1433             "server-add-service" => {
1434                 let service_uuid = Uuid::from_string(HEART_RATE_SERVICE_UUID).unwrap();
1435                 let characteristic_uuid = Uuid::from_string(HEART_RATE_MEASUREMENT_UUID).unwrap();
1436                 let descriptor_uuid = Uuid::from_string(GENERIC_UUID).unwrap();
1437                 let ccc_descriptor_uuid = Uuid::from_string(CCC_DESCRIPTOR_UUID).unwrap();
1438                 let included_service_uuid = Uuid::from_string(BATTERY_SERVICE_UUID).unwrap();
1439 
1440                 let server_id = String::from(get_arg(args, 1)?)
1441                     .parse::<i32>()
1442                     .or(Err("Failed to parse server_id"))?;
1443                 let included_service_instance_id =
1444                     String::from(get_arg(args, 2)?)
1445                         .parse::<i32>()
1446                         .or(Err("Failed to parse included service instance id"))?;
1447 
1448                 let mut service = BluetoothGattService::new(
1449                     service_uuid,
1450                     0,
1451                     GattDbElementType::PrimaryService.into(),
1452                 );
1453                 let included_service = BluetoothGattService::new(
1454                     included_service_uuid,
1455                     included_service_instance_id,
1456                     GattDbElementType::IncludedService.into(),
1457                 );
1458                 let mut characteristic = BluetoothGattCharacteristic::new(
1459                     characteristic_uuid,
1460                     0,
1461                     BluetoothGattCharacteristic::PROPERTY_READ
1462                         | BluetoothGattCharacteristic::PROPERTY_WRITE
1463                         | BluetoothGattCharacteristic::PROPERTY_NOTIFY,
1464                     BluetoothGattCharacteristic::PERMISSION_READ
1465                         | BluetoothGattCharacteristic::PERMISSION_WRITE,
1466                 );
1467                 let descriptor = BluetoothGattDescriptor::new(
1468                     descriptor_uuid,
1469                     0,
1470                     BluetoothGattCharacteristic::PERMISSION_READ
1471                         | BluetoothGattCharacteristic::PERMISSION_WRITE,
1472                 );
1473                 let ccc_descriptor = BluetoothGattDescriptor::new(
1474                     ccc_descriptor_uuid,
1475                     0,
1476                     BluetoothGattCharacteristic::PERMISSION_READ
1477                         | BluetoothGattCharacteristic::PERMISSION_WRITE,
1478                 );
1479 
1480                 service.included_services.push(included_service);
1481                 characteristic.descriptors.push(ccc_descriptor);
1482                 characteristic.descriptors.push(descriptor);
1483                 service.characteristics.push(characteristic);
1484 
1485                 self.lock_context().gatt_dbus.as_mut().unwrap().add_service(server_id, service);
1486             }
1487             "server-remove-service" => {
1488                 let server_id = String::from(get_arg(args, 1)?)
1489                     .parse::<i32>()
1490                     .or(Err("Failed to parse server_id"))?;
1491                 let service_handle = String::from(get_arg(args, 1)?)
1492                     .parse::<i32>()
1493                     .or(Err("Failed to parse service handle"))?;
1494 
1495                 self.lock_context()
1496                     .gatt_dbus
1497                     .as_mut()
1498                     .unwrap()
1499                     .remove_service(server_id, service_handle);
1500             }
1501             "server-clear-all-services" => {
1502                 let server_id = String::from(get_arg(args, 1)?)
1503                     .parse::<i32>()
1504                     .or(Err("Failed to parse server_id"))?;
1505                 self.lock_context().gatt_dbus.as_mut().unwrap().clear_services(server_id);
1506             }
1507             "server-send-response" => {
1508                 let server_id = String::from(get_arg(args, 1)?)
1509                     .parse::<i32>()
1510                     .or(Err("Failed to parse server_id"))?;
1511                 let status = match String::from(get_arg(args, 2)?).as_str() {
1512                     "success" => GattStatus::Success,
1513                     "fail" => GattStatus::Error,
1514                     _ => return Err("{} is not one of the following: `success`, `fail`".into()),
1515                 };
1516 
1517                 let request = match self.lock_context().pending_gatt_request.clone() {
1518                     None => return Err("No pending request to send response to".into()),
1519                     Some(r) => r,
1520                 };
1521                 // SAFETY: Initialized all values of the BtGattResponse object
1522                 unsafe {
1523                     self.lock_context().gatt_dbus.as_mut().unwrap().send_response(
1524                         server_id,
1525                         request.address,
1526                         request.id,
1527                         status,
1528                         request.offset,
1529                         request.value.clone(),
1530                     );
1531                 }
1532 
1533                 self.lock_context().pending_gatt_request = None;
1534             }
1535             "server-set-direct-connect" => {
1536                 let is_direct = String::from(get_arg(args, 1)?)
1537                     .parse::<bool>()
1538                     .or(Err("Failed to parse is_direct"))?;
1539 
1540                 self.lock_context().gatt_server_context.is_connect_direct = is_direct;
1541             }
1542             "server-set-connect-transport" => {
1543                 let transport = match &get_arg(args, 1)?[..] {
1544                     "Bredr" => BtTransport::Bredr,
1545                     "LE" => BtTransport::Le,
1546                     "Auto" => BtTransport::Auto,
1547                     _ => {
1548                         return Err("Failed to parse transport".into());
1549                     }
1550                 };
1551                 self.lock_context().gatt_server_context.connect_transport = transport;
1552             }
1553             _ => return Err(CommandError::InvalidArgs),
1554         }
1555         Ok(())
1556     }
1557 
cmd_le_scan(&mut self, args: &[String]) -> CommandResult1558     fn cmd_le_scan(&mut self, args: &[String]) -> CommandResult {
1559         if !self.lock_context().adapter_ready {
1560             return Err(self.adapter_not_ready());
1561         }
1562 
1563         let command = get_arg(args, 0)?;
1564 
1565         match &command[..] {
1566             "register-scanner" => {
1567                 let scanner_callback_id = self
1568                     .lock_context()
1569                     .scanner_callback_id
1570                     .ok_or("Cannot register scanner before registering scanner callback")?;
1571 
1572                 let uuid = self
1573                     .lock_context()
1574                     .gatt_dbus
1575                     .as_mut()
1576                     .unwrap()
1577                     .register_scanner(scanner_callback_id);
1578 
1579                 print_info!("Scanner to be registered with UUID = {}", uuid);
1580             }
1581             "unregister-scanner" => {
1582                 let scanner_id = String::from(get_arg(args, 1)?)
1583                     .parse::<u8>()
1584                     .or(Err("Failed parsing scanner id"))?;
1585 
1586                 self.lock_context().gatt_dbus.as_mut().unwrap().unregister_scanner(scanner_id);
1587             }
1588             "start-scan" => {
1589                 let scanner_id = String::from(get_arg(args, 1)?)
1590                     .parse::<u8>()
1591                     .or(Err("Failed parsing scanner id"))?;
1592 
1593                 self.lock_context().gatt_dbus.as_mut().unwrap().start_scan(
1594                     scanner_id,
1595                     // TODO(b/254870159): Construct real settings and filters depending on
1596                     // command line options.
1597                     None,
1598                     Some(btstack::bluetooth_gatt::ScanFilter {
1599                         rssi_high_threshold: 0,
1600                         rssi_low_threshold: 0,
1601                         rssi_low_timeout: 0,
1602                         rssi_sampling_period: 0,
1603                         condition: btstack::bluetooth_gatt::ScanFilterCondition::Patterns(vec![]),
1604                     }),
1605                 );
1606 
1607                 self.lock_context().active_scanner_ids.insert(scanner_id);
1608             }
1609             "stop-scan" => {
1610                 let scanner_id = String::from(get_arg(args, 1)?)
1611                     .parse::<u8>()
1612                     .or(Err("Failed parsing scanner id"))?;
1613 
1614                 self.lock_context().gatt_dbus.as_mut().unwrap().stop_scan(scanner_id);
1615                 self.lock_context().active_scanner_ids.remove(&scanner_id);
1616             }
1617             _ => return Err(CommandError::InvalidArgs),
1618         }
1619 
1620         Ok(())
1621     }
1622 
1623     // TODO(b/233128828): More options will be implemented to test BLE advertising.
1624     // Such as setting advertising parameters, starting multiple advertising sets, etc.
cmd_advertise(&mut self, args: &[String]) -> CommandResult1625     fn cmd_advertise(&mut self, args: &[String]) -> CommandResult {
1626         if !self.lock_context().adapter_ready {
1627             return Err(self.adapter_not_ready());
1628         }
1629 
1630         if self.lock_context().advertiser_callback_id.is_none() {
1631             return Err("No advertiser callback registered".into());
1632         }
1633 
1634         let callback_id = self.lock_context().advertiser_callback_id.unwrap();
1635 
1636         let command = get_arg(args, 0)?;
1637 
1638         match &command[..] {
1639             "on" => {
1640                 print_info!("Creating legacy advertising set...");
1641                 let s = AdvSet::new(true); // legacy advertising
1642                 AdvSet::start(self.context.clone(), s, callback_id);
1643             }
1644             "off" => {
1645                 AdvSet::stop_all(self.context.clone());
1646             }
1647             "ext" => {
1648                 print_info!("Creating extended advertising set...");
1649                 let s = AdvSet::new(false); // extended advertising
1650                 AdvSet::start(self.context.clone(), s, callback_id);
1651             }
1652             "set-interval" => {
1653                 let ms = String::from(get_arg(args, 1)?).parse::<i32>();
1654                 if ms.is_err() {
1655                     return Err("Failed parsing interval".into());
1656                 }
1657                 let interval = ms.unwrap() * 8 / 5; // in 0.625 ms.
1658 
1659                 let mut context = self.lock_context();
1660                 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.interval = interval);
1661 
1662                 // To avoid borrowing context as mutable from an immutable borrow.
1663                 // Required information is collected in advance and then passed
1664                 // to the D-Bus call which requires a mutable borrow.
1665                 let advs: Vec<(_, _)> = context
1666                     .adv_sets
1667                     .iter()
1668                     .filter_map(|(_, s)| s.adv_id.map(|adv_id| (adv_id, s.params.clone())))
1669                     .collect();
1670                 for (adv_id, params) in advs {
1671                     print_info!("Setting advertising parameters for {}", adv_id);
1672                     context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1673                 }
1674             }
1675             "set-connectable" => {
1676                 let connectable = match &get_arg(args, 1)?[..] {
1677                     "on" => true,
1678                     "off" => false,
1679                     _ => false,
1680                 };
1681 
1682                 let adv_id = String::from(get_arg(args, 2)?)
1683                     .parse::<i32>()
1684                     .or(Err("Failed parsing adv_id"))?;
1685 
1686                 let mut context = self.context.lock().unwrap();
1687 
1688                 let advs: Vec<(_, _)> = context
1689                     .adv_sets
1690                     .iter_mut()
1691                     .filter_map(|(_, s)| {
1692                         if !(s.adv_id.map_or(false, |id| id == adv_id)) {
1693                             return None;
1694                         }
1695                         s.params.connectable = connectable;
1696                         Some((s.params.clone(), s.data.clone()))
1697                     })
1698                     .collect();
1699 
1700                 for (params, data) in advs {
1701                     print_info!("Setting advertising parameters for {}", adv_id);
1702                     context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1703 
1704                     // renew the flags
1705                     print_info!("Setting advertising data for {}", adv_id);
1706                     context.gatt_dbus.as_mut().unwrap().set_advertising_data(adv_id, data);
1707                 }
1708             }
1709             "set-scan-rsp" => {
1710                 let enable = match &get_arg(args, 1)?[..] {
1711                     "enable" => true,
1712                     "disable" => false,
1713                     _ => false,
1714                 };
1715 
1716                 let mut context = self.lock_context();
1717                 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.scannable = enable);
1718 
1719                 let advs: Vec<(_, _, _)> = context
1720                     .adv_sets
1721                     .iter()
1722                     .filter_map(|(_, s)| {
1723                         s.adv_id.map(|adv_id| (adv_id, s.params.clone(), s.scan_rsp.clone()))
1724                     })
1725                     .collect();
1726                 for (adv_id, params, scan_rsp) in advs {
1727                     print_info!("Setting scan response data for {}", adv_id);
1728                     context.gatt_dbus.as_mut().unwrap().set_scan_response_data(adv_id, scan_rsp);
1729                     print_info!("Setting parameters for {}", adv_id);
1730                     context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1731                 }
1732             }
1733             "set-raw-data" => {
1734                 let data = hex::decode(get_arg(args, 1)?).or(Err("Failed parsing data"))?;
1735 
1736                 let adv_id = String::from(get_arg(args, 2)?)
1737                     .parse::<i32>()
1738                     .or(Err("Failed parsing adv_id"))?;
1739 
1740                 let mut context = self.context.lock().unwrap();
1741                 if !context.adv_sets.iter().any(|(_, s)| s.adv_id.map_or(false, |id| id == adv_id))
1742                 {
1743                     return Err("Failed to find advertising set".into());
1744                 }
1745 
1746                 print_info!("Setting advertising data for {}", adv_id);
1747                 context.gatt_dbus.as_mut().unwrap().set_raw_adv_data(adv_id, data);
1748             }
1749             _ => return Err(CommandError::InvalidArgs),
1750         }
1751 
1752         Ok(())
1753     }
1754 
cmd_sdp(&mut self, args: &[String]) -> CommandResult1755     fn cmd_sdp(&mut self, args: &[String]) -> CommandResult {
1756         if !self.lock_context().adapter_ready {
1757             return Err(self.adapter_not_ready());
1758         }
1759 
1760         let command = get_arg(args, 0)?;
1761 
1762         match &command[..] {
1763             "search" => {
1764                 let device = BluetoothDevice {
1765                     address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
1766                     name: String::from(""),
1767                 };
1768                 let uuid = Uuid::from_string(get_arg(args, 2)?).ok_or("Invalid UUID")?;
1769                 let success =
1770                     self.lock_context().adapter_dbus.as_ref().unwrap().sdp_search(device, uuid);
1771                 if !success {
1772                     return Err("Unable to execute SDP search".into());
1773                 }
1774             }
1775             _ => return Err(CommandError::InvalidArgs),
1776         }
1777         Ok(())
1778     }
1779 
cmd_socket(&mut self, args: &[String]) -> CommandResult1780     fn cmd_socket(&mut self, args: &[String]) -> CommandResult {
1781         if !self.lock_context().adapter_ready {
1782             return Err(self.adapter_not_ready());
1783         }
1784 
1785         let callback_id = match self.lock_context().socket_manager_callback_id {
1786             Some(id) => id,
1787             None => {
1788                 return Err("No socket manager callback registered.".into());
1789             }
1790         };
1791 
1792         let command = get_arg(args, 0)?;
1793 
1794         match &command[..] {
1795             "set-on-connect-schedule" => {
1796                 let schedule = match &get_arg(args, 1)?[..] {
1797                     "send" => SocketSchedule {
1798                         num_frame: 1,
1799                         send_interval: Duration::from_millis(0),
1800                         disconnect_delay: Duration::from_secs(30),
1801                     },
1802                     "resend" => SocketSchedule {
1803                         num_frame: 3,
1804                         send_interval: Duration::from_millis(100),
1805                         disconnect_delay: Duration::from_secs(30),
1806                     },
1807                     "dump" => SocketSchedule {
1808                         num_frame: 0,
1809                         send_interval: Duration::from_millis(0),
1810                         disconnect_delay: Duration::from_secs(30),
1811                     },
1812                     _ => {
1813                         return Err("Failed to parse schedule".into());
1814                     }
1815                 };
1816 
1817                 self.context.lock().unwrap().socket_test_schedule = Some(schedule);
1818             }
1819             "send-msc" => {
1820                 let dlci =
1821                     String::from(get_arg(args, 1)?).parse::<u8>().or(Err("Failed parsing DLCI"))?;
1822                 let addr = RawAddress::from_string(get_arg(args, 2)?).ok_or("Invalid Address")?;
1823                 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().rfcomm_send_msc(dlci, addr);
1824             }
1825             "listen-rfcomm" => {
1826                 let scn = String::from(get_arg(args, 1)?)
1827                     .parse::<i32>()
1828                     .or(Err("Failed parsing Service Channel Number"))?;
1829                 let SocketResult { status, id } = self
1830                     .context
1831                     .lock()
1832                     .unwrap()
1833                     .socket_manager_dbus
1834                     .as_mut()
1835                     .unwrap()
1836                     .listen_using_rfcomm(callback_id, Some(scn), None, None, None);
1837                 if status != BtStatus::Success {
1838                     return Err(format!(
1839                         "Failed to request for listening using rfcomm, status = {:?}",
1840                         status,
1841                     )
1842                     .into());
1843                 }
1844                 print_info!("Requested for listening using rfcomm on socket {}", id);
1845             }
1846             "listen" => {
1847                 let auth_required = String::from(get_arg(args, 1)?)
1848                     .parse::<bool>()
1849                     .or(Err("Failed to parse auth-required"))?;
1850                 let is_le = match &get_arg(args, 2)?[..] {
1851                     "LE" => true,
1852                     "Bredr" => false,
1853                     _ => {
1854                         return Err("Failed to parse socket type".into());
1855                     }
1856                 };
1857 
1858                 let SocketResult { status, id } = {
1859                     let mut context_proxy = self.context.lock().unwrap();
1860                     let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1861                     if auth_required {
1862                         if is_le {
1863                             proxy.listen_using_l2cap_le_channel(callback_id)
1864                         } else {
1865                             proxy.listen_using_l2cap_channel(callback_id)
1866                         }
1867                     } else if is_le {
1868                         proxy.listen_using_insecure_l2cap_le_channel(callback_id)
1869                     } else {
1870                         proxy.listen_using_insecure_l2cap_channel(callback_id)
1871                     }
1872                 };
1873 
1874                 if status != BtStatus::Success {
1875                     return Err(format!(
1876                         "Failed to request for listening using l2cap channel, status = {:?}",
1877                         status,
1878                     )
1879                     .into());
1880                 }
1881                 print_info!("Requested for listening using l2cap channel on socket {}", id);
1882             }
1883             "connect" => {
1884                 let (addr, sock_type, psm_or_uuid) =
1885                     (&get_arg(args, 1)?, &get_arg(args, 2)?, &get_arg(args, 3)?);
1886                 let device = BluetoothDevice {
1887                     address: RawAddress::from_string(*addr).ok_or("Invalid Address")?,
1888                     name: String::from("Socket Connect Device"),
1889                 };
1890 
1891                 let auth_required = String::from(get_arg(args, 4)?)
1892                     .parse::<bool>()
1893                     .or(Err("Failed to parse auth-required"))?;
1894 
1895                 let is_le = match &get_arg(args, 5)?[..] {
1896                     "LE" => true,
1897                     "Bredr" => false,
1898                     _ => {
1899                         return Err("Failed to parse socket type".into());
1900                     }
1901                 };
1902 
1903                 let SocketResult { status, id } = {
1904                     let mut context_proxy = self.context.lock().unwrap();
1905                     let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1906 
1907                     match &sock_type[0..] {
1908                         "l2cap" => {
1909                             let psm = match psm_or_uuid.parse::<i32>() {
1910                                 Ok(v) => v,
1911                                 Err(e) => {
1912                                     return Err(CommandError::Failed(format!(
1913                                         "Bad PSM given. Error={}",
1914                                         e
1915                                     )));
1916                                 }
1917                             };
1918 
1919                             if auth_required {
1920                                 if is_le {
1921                                     proxy.create_l2cap_le_channel(callback_id, device, psm)
1922                                 } else {
1923                                     proxy.create_l2cap_channel(callback_id, device, psm)
1924                                 }
1925                             } else if is_le {
1926                                 proxy.create_insecure_l2cap_le_channel(callback_id, device, psm)
1927                             } else {
1928                                 proxy.create_insecure_l2cap_channel(callback_id, device, psm)
1929                             }
1930                         }
1931                         "rfcomm" => {
1932                             let uuid = match Uuid::from_string(*psm_or_uuid) {
1933                                 Some(uu) => uu,
1934                                 None => {
1935                                     return Err(CommandError::Failed(
1936                                         "Could not parse given uuid.".to_string(),
1937                                     ));
1938                                 }
1939                             };
1940 
1941                             if auth_required {
1942                                 proxy.create_rfcomm_socket_to_service_record(
1943                                     callback_id,
1944                                     device,
1945                                     uuid,
1946                                 )
1947                             } else {
1948                                 proxy.create_insecure_rfcomm_socket_to_service_record(
1949                                     callback_id,
1950                                     device,
1951                                     uuid,
1952                                 )
1953                             }
1954                         }
1955                         _ => {
1956                             return Err(CommandError::Failed(format!(
1957                                 "Unknown socket type: {}",
1958                                 sock_type
1959                             )));
1960                         }
1961                     }
1962                 };
1963 
1964                 if status != BtStatus::Success {
1965                     return Err(CommandError::Failed(format!("Failed to create socket with status={:?} against {}, type {}, with psm/uuid {}",
1966                         status, addr, sock_type, psm_or_uuid)));
1967                 } else {
1968                     print_info!("Called create socket with result ({:?}, {}) against {}, type {}, with psm/uuid {}",
1969                     status, id, addr, sock_type, psm_or_uuid);
1970                 }
1971             }
1972             "close" => {
1973                 let sockid = String::from(get_arg(args, 1)?)
1974                     .parse::<u64>()
1975                     .or(Err("Failed parsing socket ID"))?;
1976                 let status = self
1977                     .context
1978                     .lock()
1979                     .unwrap()
1980                     .socket_manager_dbus
1981                     .as_mut()
1982                     .unwrap()
1983                     .close(callback_id, sockid);
1984                 if status != BtStatus::Success {
1985                     return Err(format!(
1986                         "Failed to close the listening socket, status = {:?}",
1987                         status,
1988                     )
1989                     .into());
1990                 }
1991             }
1992 
1993             _ => return Err(CommandError::InvalidArgs),
1994         };
1995 
1996         Ok(())
1997     }
1998 
cmd_hid(&mut self, args: &[String]) -> CommandResult1999     fn cmd_hid(&mut self, args: &[String]) -> CommandResult {
2000         if !self.context.lock().unwrap().adapter_ready {
2001             return Err(self.adapter_not_ready());
2002         }
2003 
2004         let command = get_arg(args, 0)?;
2005 
2006         match &command[..] {
2007             "get-report" => {
2008                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2009                 let report_type = match &get_arg(args, 2)?[..] {
2010                     "Input" => BthhReportType::InputReport,
2011                     "Output" => BthhReportType::OutputReport,
2012                     "Feature" => BthhReportType::FeatureReport,
2013                     _ => {
2014                         return Err("Failed to parse report type".into());
2015                     }
2016                 };
2017                 let report_id = String::from(get_arg(args, 3)?)
2018                     .parse::<u8>()
2019                     .or(Err("Failed parsing report_id"))?;
2020 
2021                 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().get_hid_report(
2022                     addr,
2023                     report_type,
2024                     report_id,
2025                 );
2026             }
2027             "set-report" => {
2028                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2029                 let report_type = match &get_arg(args, 2)?[..] {
2030                     "Input" => BthhReportType::InputReport,
2031                     "Output" => BthhReportType::OutputReport,
2032                     "Feature" => BthhReportType::FeatureReport,
2033                     _ => {
2034                         return Err("Failed to parse report type".into());
2035                     }
2036                 };
2037                 let report_value = String::from(get_arg(args, 3)?);
2038 
2039                 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().set_hid_report(
2040                     addr,
2041                     report_type,
2042                     report_value,
2043                 );
2044             }
2045             "send-data" => {
2046                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2047                 let data = String::from(get_arg(args, 2)?);
2048 
2049                 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().send_hid_data(addr, data);
2050             }
2051             "virtual-unplug" => {
2052                 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2053                 self.context
2054                     .lock()
2055                     .unwrap()
2056                     .qa_dbus
2057                     .as_mut()
2058                     .unwrap()
2059                     .send_hid_virtual_unplug(addr);
2060             }
2061             _ => return Err(CommandError::InvalidArgs),
2062         };
2063 
2064         Ok(())
2065     }
2066 
2067     /// Get the list of rules of supported commands
get_command_rule_list(&self) -> Vec<String>2068     pub fn get_command_rule_list(&self) -> Vec<String> {
2069         self.command_options.values().flat_map(|cmd| cmd.rules.clone()).collect()
2070     }
2071 
cmd_list_devices(&mut self, args: &[String]) -> CommandResult2072     fn cmd_list_devices(&mut self, args: &[String]) -> CommandResult {
2073         if !self.lock_context().adapter_ready {
2074             return Err(self.adapter_not_ready());
2075         }
2076 
2077         let command = get_arg(args, 0)?;
2078 
2079         match &command[..] {
2080             "bonded" => {
2081                 print_info!("Known bonded devices:");
2082                 let devices =
2083                     self.lock_context().adapter_dbus.as_ref().unwrap().get_bonded_devices();
2084                 for device in devices.iter() {
2085                     print_info!("[{}] {}", device.address.to_string(), device.name);
2086                 }
2087             }
2088             "found" => {
2089                 print_info!("Devices found in most recent discovery session:");
2090                 for (key, val) in self.lock_context().found_devices.iter() {
2091                     print_info!("[{:17}] {}", key, val.name);
2092                 }
2093             }
2094             "connected" => {
2095                 print_info!("Connected devices:");
2096                 let devices =
2097                     self.lock_context().adapter_dbus.as_ref().unwrap().get_connected_devices();
2098                 for device in devices.iter() {
2099                     print_info!("[{}] {}", device.address.to_string(), device.name);
2100                 }
2101             }
2102             other => {
2103                 println!("Invalid argument '{}'", other);
2104             }
2105         }
2106 
2107         Ok(())
2108     }
2109 
cmd_telephony(&mut self, args: &[String]) -> CommandResult2110     fn cmd_telephony(&mut self, args: &[String]) -> CommandResult {
2111         if !self.context.lock().unwrap().adapter_ready {
2112             return Err(self.adapter_not_ready());
2113         }
2114 
2115         match &get_arg(args, 0)?[..] {
2116             "set-network" => {
2117                 self.context
2118                     .lock()
2119                     .unwrap()
2120                     .telephony_dbus
2121                     .as_mut()
2122                     .unwrap()
2123                     .set_network_available(match &get_arg(args, 1)?[..] {
2124                         "on" => true,
2125                         "off" => false,
2126                         other => {
2127                             return Err(format!("Invalid argument '{}'", other).into());
2128                         }
2129                     });
2130             }
2131             "set-roaming" => {
2132                 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().set_roaming(
2133                     match &get_arg(args, 1)?[..] {
2134                         "on" => true,
2135                         "off" => false,
2136                         other => {
2137                             return Err(format!("Invalid argument '{}'", other).into());
2138                         }
2139                     },
2140                 );
2141             }
2142             "set-signal" => {
2143                 let strength = String::from(get_arg(args, 1)?)
2144                     .parse::<i32>()
2145                     .or(Err("Failed parsing signal strength"))?;
2146                 if !(0..=5).contains(&strength) {
2147                     return Err(
2148                         format!("Invalid signal strength, got {}, want 0 to 5", strength).into()
2149                     );
2150                 }
2151                 self.context
2152                     .lock()
2153                     .unwrap()
2154                     .telephony_dbus
2155                     .as_mut()
2156                     .unwrap()
2157                     .set_signal_strength(strength);
2158             }
2159             "set-battery" => {
2160                 let level = String::from(get_arg(args, 1)?)
2161                     .parse::<i32>()
2162                     .or(Err("Failed parsing battery level"))?;
2163                 if !(0..=5).contains(&level) {
2164                     return Err(format!("Invalid battery level, got {}, want 0 to 5", level).into());
2165                 }
2166                 self.context
2167                     .lock()
2168                     .unwrap()
2169                     .telephony_dbus
2170                     .as_mut()
2171                     .unwrap()
2172                     .set_battery_level(level);
2173             }
2174             "enable" => {
2175                 let mut context = self.lock_context();
2176                 context.telephony_dbus.as_mut().unwrap().set_mps_qualification_enabled(true);
2177                 if context.mps_sdp_handle.is_none() {
2178                     let success = context
2179                         .adapter_dbus
2180                         .as_mut()
2181                         .unwrap()
2182                         .create_sdp_record(BtSdpRecord::Mps(BtSdpMpsRecord::default()));
2183                     if !success {
2184                         return Err("Failed to create SDP record".to_string().into());
2185                     }
2186                 }
2187             }
2188             "disable" => {
2189                 let mut context = self.lock_context();
2190                 context.telephony_dbus.as_mut().unwrap().set_mps_qualification_enabled(false);
2191                 if let Some(handle) = context.mps_sdp_handle.take() {
2192                     let success = context.adapter_dbus.as_mut().unwrap().remove_sdp_record(handle);
2193                     if !success {
2194                         return Err("Failed to remove SDP record".to_string().into());
2195                     }
2196                 }
2197             }
2198             "set-phone-ops" => {
2199                 let on_or_off = match &get_arg(args, 1)?[..] {
2200                     "on" => true,
2201                     "off" => false,
2202                     _ => {
2203                         return Err("Failed to parse on|off".into());
2204                     }
2205                 };
2206                 self.context
2207                     .lock()
2208                     .unwrap()
2209                     .telephony_dbus
2210                     .as_mut()
2211                     .unwrap()
2212                     .set_phone_ops_enabled(on_or_off);
2213             }
2214             "incoming-call" => {
2215                 let success = self
2216                     .context
2217                     .lock()
2218                     .unwrap()
2219                     .telephony_dbus
2220                     .as_mut()
2221                     .unwrap()
2222                     .incoming_call(String::from(get_arg(args, 1)?));
2223                 if !success {
2224                     return Err("IncomingCall failed".into());
2225                 }
2226             }
2227             "dialing-call" => {
2228                 let success = self
2229                     .context
2230                     .lock()
2231                     .unwrap()
2232                     .telephony_dbus
2233                     .as_mut()
2234                     .unwrap()
2235                     .dialing_call(String::from(get_arg(args, 1)?));
2236                 if !success {
2237                     return Err("DialingCall failed".into());
2238                 }
2239             }
2240             "answer-call" => {
2241                 let success =
2242                     self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().answer_call();
2243                 if !success {
2244                     return Err("AnswerCall failed".into());
2245                 }
2246             }
2247             "hangup-call" => {
2248                 let success =
2249                     self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().hangup_call();
2250                 if !success {
2251                     return Err("HangupCall failed".into());
2252                 }
2253             }
2254             "set-memory-call" => {
2255                 let success = self
2256                     .context
2257                     .lock()
2258                     .unwrap()
2259                     .telephony_dbus
2260                     .as_mut()
2261                     .unwrap()
2262                     .set_memory_call(get_arg(args, 1).ok().map(String::from));
2263                 if !success {
2264                     return Err("SetMemoryCall failed".into());
2265                 }
2266             }
2267             "set-last-call" => {
2268                 let success = self
2269                     .context
2270                     .lock()
2271                     .unwrap()
2272                     .telephony_dbus
2273                     .as_mut()
2274                     .unwrap()
2275                     .set_last_call(get_arg(args, 1).ok().map(String::from));
2276                 if !success {
2277                     return Err("SetLastCall failed".into());
2278                 }
2279             }
2280             "release-held" => {
2281                 let success =
2282                     self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().release_held();
2283                 if !success {
2284                     return Err("ReleaseHeld failed".into());
2285                 }
2286             }
2287             "release-active-accept-held" => {
2288                 let success = self
2289                     .context
2290                     .lock()
2291                     .unwrap()
2292                     .telephony_dbus
2293                     .as_mut()
2294                     .unwrap()
2295                     .release_active_accept_held();
2296                 if !success {
2297                     return Err("ReleaseActiveAcceptHeld failed".into());
2298                 }
2299             }
2300             "hold-active-accept-held" => {
2301                 let success = self
2302                     .context
2303                     .lock()
2304                     .unwrap()
2305                     .telephony_dbus
2306                     .as_mut()
2307                     .unwrap()
2308                     .hold_active_accept_held();
2309                 if !success {
2310                     return Err("HoldActiveAcceptHeld failed".into());
2311                 }
2312             }
2313             "audio-connect" => {
2314                 let success =
2315                     self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().audio_connect(
2316                         RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
2317                     );
2318                 if !success {
2319                     return Err("ConnectAudio failed".into());
2320                 }
2321             }
2322             "audio-disconnect" => {
2323                 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().audio_disconnect(
2324                     RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
2325                 );
2326             }
2327             other => {
2328                 return Err(format!("Invalid argument '{}'", other).into());
2329             }
2330         }
2331         Ok(())
2332     }
2333 
cmd_qa(&mut self, args: &[String]) -> CommandResult2334     fn cmd_qa(&mut self, args: &[String]) -> CommandResult {
2335         if !self.context.lock().unwrap().adapter_ready {
2336             return Err(self.adapter_not_ready());
2337         }
2338 
2339         let command = get_arg(args, 0)?;
2340 
2341         match &command[..] {
2342             "add-media-player" => {
2343                 let name = String::from(get_arg(args, 1)?);
2344                 let browsing_supported = String::from(get_arg(args, 2)?)
2345                     .parse::<bool>()
2346                     .or(Err("Failed to parse browsing_supported"))?;
2347                 self.context
2348                     .lock()
2349                     .unwrap()
2350                     .qa_dbus
2351                     .as_mut()
2352                     .unwrap()
2353                     .add_media_player(name, browsing_supported);
2354             }
2355             _ => return Err(CommandError::InvalidArgs),
2356         };
2357 
2358         Ok(())
2359     }
2360 
cmd_media(&mut self, args: &[String]) -> CommandResult2361     fn cmd_media(&mut self, args: &[String]) -> CommandResult {
2362         if !self.context.lock().unwrap().adapter_ready {
2363             return Err(self.adapter_not_ready());
2364         }
2365 
2366         match &get_arg(args, 0)?[..] {
2367             "log" => {
2368                 self.context.lock().unwrap().media_dbus.as_mut().unwrap().trigger_debug_dump();
2369             }
2370             other => {
2371                 return Err(format!("Invalid argument '{}'", other).into());
2372             }
2373         }
2374 
2375         Ok(())
2376     }
2377 
cmd_dumpsys(&mut self, _args: &[String]) -> CommandResult2378     fn cmd_dumpsys(&mut self, _args: &[String]) -> CommandResult {
2379         if !self.lock_context().adapter_ready {
2380             return Err(self.adapter_not_ready());
2381         }
2382 
2383         let contents = self.lock_context().adapter_dbus.as_mut().unwrap().get_dumpsys();
2384         println!("{}", contents);
2385 
2386         Ok(())
2387     }
2388 
cmd_log(&mut self, args: &[String]) -> CommandResult2389     fn cmd_log(&mut self, args: &[String]) -> CommandResult {
2390         if !self.lock_context().adapter_ready {
2391             return Err(self.adapter_not_ready());
2392         }
2393 
2394         let command = get_arg(args, 0)?;
2395 
2396         match &command[..] {
2397             "set-level" => {
2398                 let level = match &get_arg(args, 1)?[..] {
2399                     "info" => Level::Info,
2400                     "debug" => Level::Debug,
2401                     "verbose" => Level::Verbose,
2402                     _ => {
2403                         return Err("Failed to parse log level".into());
2404                     }
2405                 };
2406                 self.lock_context().logging_dbus.as_mut().unwrap().set_log_level(level);
2407             }
2408 
2409             "get-level" => {
2410                 let level = self.lock_context().logging_dbus.as_ref().unwrap().get_log_level();
2411 
2412                 print_info!("log level: {:?}", level);
2413             }
2414 
2415             other => {
2416                 return Err(format!("Invalid argument '{}'", other).into());
2417             }
2418         }
2419 
2420         Ok(())
2421     }
2422 }
2423 
2424 #[cfg(test)]
2425 mod tests {
2426 
2427     use super::*;
2428 
2429     #[test]
test_wrap_help_text()2430     fn test_wrap_help_text() {
2431         let text = "hello";
2432         let text_len = text.chars().count();
2433         // ensure no overflow
2434         assert_eq!(format!("|{}|", text), wrap_help_text(text, 4, 0));
2435         assert_eq!(format!("|{}|", text), wrap_help_text(text, 5, 0));
2436         assert_eq!(format!("|{}{}|", text, " "), wrap_help_text(text, 6, 0));
2437         assert_eq!(format!("|{}{}|", text, " ".repeat(2)), wrap_help_text(text, 7, 0));
2438         assert_eq!(
2439             format!("|{}{}|", text, " ".repeat(100 - text_len)),
2440             wrap_help_text(text, 100, 0)
2441         );
2442         assert_eq!(format!("|{}{}|", " ", text), wrap_help_text(text, 4, 1));
2443         assert_eq!(format!("|{}{}|", " ".repeat(2), text), wrap_help_text(text, 5, 2));
2444         assert_eq!(format!("|{}{}{}|", " ".repeat(3), text, " "), wrap_help_text(text, 6, 3));
2445         assert_eq!(
2446             format!("|{}{}{}|", " ".repeat(4), text, " ".repeat(7 - text_len)),
2447             wrap_help_text(text, 7, 4)
2448         );
2449         assert_eq!(format!("|{}{}|", " ".repeat(9), text), wrap_help_text(text, 4, 9));
2450         assert_eq!(format!("|{}{}|", " ".repeat(10), text), wrap_help_text(text, 3, 10));
2451         assert_eq!(format!("|{}{}|", " ".repeat(11), text), wrap_help_text(text, 2, 11));
2452         assert_eq!(format!("|{}{}|", " ".repeat(12), text), wrap_help_text(text, 1, 12));
2453         assert_eq!("||", wrap_help_text("", 0, 0));
2454         assert_eq!("| |", wrap_help_text("", 1, 0));
2455         assert_eq!("|  |", wrap_help_text("", 1, 1));
2456         assert_eq!("| |", wrap_help_text("", 0, 1));
2457     }
2458 }
2459