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