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 self.lock_context().gatt_dbus.as_mut().unwrap().send_response(
1522 server_id,
1523 request.address,
1524 request.id,
1525 status,
1526 request.offset,
1527 request.value.clone(),
1528 );
1529
1530 self.lock_context().pending_gatt_request = None;
1531 }
1532 "server-set-direct-connect" => {
1533 let is_direct = String::from(get_arg(args, 1)?)
1534 .parse::<bool>()
1535 .or(Err("Failed to parse is_direct"))?;
1536
1537 self.lock_context().gatt_server_context.is_connect_direct = is_direct;
1538 }
1539 "server-set-connect-transport" => {
1540 let transport = match &get_arg(args, 1)?[..] {
1541 "Bredr" => BtTransport::Bredr,
1542 "LE" => BtTransport::Le,
1543 "Auto" => BtTransport::Auto,
1544 _ => {
1545 return Err("Failed to parse transport".into());
1546 }
1547 };
1548 self.lock_context().gatt_server_context.connect_transport = transport;
1549 }
1550 _ => return Err(CommandError::InvalidArgs),
1551 }
1552 Ok(())
1553 }
1554
cmd_le_scan(&mut self, args: &[String]) -> CommandResult1555 fn cmd_le_scan(&mut self, args: &[String]) -> CommandResult {
1556 if !self.lock_context().adapter_ready {
1557 return Err(self.adapter_not_ready());
1558 }
1559
1560 let command = get_arg(args, 0)?;
1561
1562 match &command[..] {
1563 "register-scanner" => {
1564 let scanner_callback_id = self
1565 .lock_context()
1566 .scanner_callback_id
1567 .ok_or("Cannot register scanner before registering scanner callback")?;
1568
1569 let uuid = self
1570 .lock_context()
1571 .gatt_dbus
1572 .as_mut()
1573 .unwrap()
1574 .register_scanner(scanner_callback_id);
1575
1576 print_info!("Scanner to be registered with UUID = {}", uuid);
1577 }
1578 "unregister-scanner" => {
1579 let scanner_id = String::from(get_arg(args, 1)?)
1580 .parse::<u8>()
1581 .or(Err("Failed parsing scanner id"))?;
1582
1583 self.lock_context().gatt_dbus.as_mut().unwrap().unregister_scanner(scanner_id);
1584 }
1585 "start-scan" => {
1586 let scanner_id = String::from(get_arg(args, 1)?)
1587 .parse::<u8>()
1588 .or(Err("Failed parsing scanner id"))?;
1589
1590 self.lock_context().gatt_dbus.as_mut().unwrap().start_scan(
1591 scanner_id,
1592 // TODO(b/254870159): Construct real settings and filters depending on
1593 // command line options.
1594 None,
1595 Some(btstack::bluetooth_gatt::ScanFilter {
1596 rssi_high_threshold: 0,
1597 rssi_low_threshold: 0,
1598 rssi_low_timeout: 0,
1599 rssi_sampling_period: 0,
1600 condition: btstack::bluetooth_gatt::ScanFilterCondition::Patterns(vec![]),
1601 }),
1602 );
1603
1604 self.lock_context().active_scanner_ids.insert(scanner_id);
1605 }
1606 "stop-scan" => {
1607 let scanner_id = String::from(get_arg(args, 1)?)
1608 .parse::<u8>()
1609 .or(Err("Failed parsing scanner id"))?;
1610
1611 self.lock_context().gatt_dbus.as_mut().unwrap().stop_scan(scanner_id);
1612 self.lock_context().active_scanner_ids.remove(&scanner_id);
1613 }
1614 _ => return Err(CommandError::InvalidArgs),
1615 }
1616
1617 Ok(())
1618 }
1619
1620 // TODO(b/233128828): More options will be implemented to test BLE advertising.
1621 // Such as setting advertising parameters, starting multiple advertising sets, etc.
cmd_advertise(&mut self, args: &[String]) -> CommandResult1622 fn cmd_advertise(&mut self, args: &[String]) -> CommandResult {
1623 if !self.lock_context().adapter_ready {
1624 return Err(self.adapter_not_ready());
1625 }
1626
1627 if self.lock_context().advertiser_callback_id.is_none() {
1628 return Err("No advertiser callback registered".into());
1629 }
1630
1631 let callback_id = self.lock_context().advertiser_callback_id.unwrap();
1632
1633 let command = get_arg(args, 0)?;
1634
1635 match &command[..] {
1636 "on" => {
1637 print_info!("Creating legacy advertising set...");
1638 let s = AdvSet::new(true); // legacy advertising
1639 AdvSet::start(self.context.clone(), s, callback_id);
1640 }
1641 "off" => {
1642 AdvSet::stop_all(self.context.clone());
1643 }
1644 "ext" => {
1645 print_info!("Creating extended advertising set...");
1646 let s = AdvSet::new(false); // extended advertising
1647 AdvSet::start(self.context.clone(), s, callback_id);
1648 }
1649 "set-interval" => {
1650 let ms = String::from(get_arg(args, 1)?).parse::<i32>();
1651 if ms.is_err() {
1652 return Err("Failed parsing interval".into());
1653 }
1654 let interval = ms.unwrap() * 8 / 5; // in 0.625 ms.
1655
1656 let mut context = self.lock_context();
1657 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.interval = interval);
1658
1659 // To avoid borrowing context as mutable from an immutable borrow.
1660 // Required information is collected in advance and then passed
1661 // to the D-Bus call which requires a mutable borrow.
1662 let advs: Vec<(_, _)> = context
1663 .adv_sets
1664 .iter()
1665 .filter_map(|(_, s)| s.adv_id.map(|adv_id| (adv_id, s.params.clone())))
1666 .collect();
1667 for (adv_id, params) in advs {
1668 print_info!("Setting advertising parameters for {}", adv_id);
1669 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1670 }
1671 }
1672 "set-connectable" => {
1673 let connectable = match &get_arg(args, 1)?[..] {
1674 "on" => true,
1675 "off" => false,
1676 _ => false,
1677 };
1678
1679 let adv_id = String::from(get_arg(args, 2)?)
1680 .parse::<i32>()
1681 .or(Err("Failed parsing adv_id"))?;
1682
1683 let mut context = self.context.lock().unwrap();
1684
1685 let advs: Vec<(_, _)> = context
1686 .adv_sets
1687 .iter_mut()
1688 .filter_map(|(_, s)| {
1689 if !(s.adv_id.map_or(false, |id| id == adv_id)) {
1690 return None;
1691 }
1692 s.params.connectable = connectable;
1693 Some((s.params.clone(), s.data.clone()))
1694 })
1695 .collect();
1696
1697 for (params, data) in advs {
1698 print_info!("Setting advertising parameters for {}", adv_id);
1699 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1700
1701 // renew the flags
1702 print_info!("Setting advertising data for {}", adv_id);
1703 context.gatt_dbus.as_mut().unwrap().set_advertising_data(adv_id, data);
1704 }
1705 }
1706 "set-scan-rsp" => {
1707 let enable = match &get_arg(args, 1)?[..] {
1708 "enable" => true,
1709 "disable" => false,
1710 _ => false,
1711 };
1712
1713 let mut context = self.lock_context();
1714 context.adv_sets.iter_mut().for_each(|(_, s)| s.params.scannable = enable);
1715
1716 let advs: Vec<(_, _, _)> = context
1717 .adv_sets
1718 .iter()
1719 .filter_map(|(_, s)| {
1720 s.adv_id.map(|adv_id| (adv_id, s.params.clone(), s.scan_rsp.clone()))
1721 })
1722 .collect();
1723 for (adv_id, params, scan_rsp) in advs {
1724 print_info!("Setting scan response data for {}", adv_id);
1725 context.gatt_dbus.as_mut().unwrap().set_scan_response_data(adv_id, scan_rsp);
1726 print_info!("Setting parameters for {}", adv_id);
1727 context.gatt_dbus.as_mut().unwrap().set_advertising_parameters(adv_id, params);
1728 }
1729 }
1730 "set-raw-data" => {
1731 let data = hex::decode(get_arg(args, 1)?).or(Err("Failed parsing data"))?;
1732
1733 let adv_id = String::from(get_arg(args, 2)?)
1734 .parse::<i32>()
1735 .or(Err("Failed parsing adv_id"))?;
1736
1737 let mut context = self.context.lock().unwrap();
1738 if !context.adv_sets.iter().any(|(_, s)| s.adv_id.map_or(false, |id| id == adv_id))
1739 {
1740 return Err("Failed to find advertising set".into());
1741 }
1742
1743 print_info!("Setting advertising data for {}", adv_id);
1744 context.gatt_dbus.as_mut().unwrap().set_raw_adv_data(adv_id, data);
1745 }
1746 _ => return Err(CommandError::InvalidArgs),
1747 }
1748
1749 Ok(())
1750 }
1751
cmd_sdp(&mut self, args: &[String]) -> CommandResult1752 fn cmd_sdp(&mut self, args: &[String]) -> CommandResult {
1753 if !self.lock_context().adapter_ready {
1754 return Err(self.adapter_not_ready());
1755 }
1756
1757 let command = get_arg(args, 0)?;
1758
1759 match &command[..] {
1760 "search" => {
1761 let device = BluetoothDevice {
1762 address: RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
1763 name: String::from(""),
1764 };
1765 let uuid = Uuid::from_string(get_arg(args, 2)?).ok_or("Invalid UUID")?;
1766 let success =
1767 self.lock_context().adapter_dbus.as_ref().unwrap().sdp_search(device, uuid);
1768 if !success {
1769 return Err("Unable to execute SDP search".into());
1770 }
1771 }
1772 _ => return Err(CommandError::InvalidArgs),
1773 }
1774 Ok(())
1775 }
1776
cmd_socket(&mut self, args: &[String]) -> CommandResult1777 fn cmd_socket(&mut self, args: &[String]) -> CommandResult {
1778 if !self.lock_context().adapter_ready {
1779 return Err(self.adapter_not_ready());
1780 }
1781
1782 let callback_id = match self.lock_context().socket_manager_callback_id {
1783 Some(id) => id,
1784 None => {
1785 return Err("No socket manager callback registered.".into());
1786 }
1787 };
1788
1789 let command = get_arg(args, 0)?;
1790
1791 match &command[..] {
1792 "set-on-connect-schedule" => {
1793 let schedule = match &get_arg(args, 1)?[..] {
1794 "send" => SocketSchedule {
1795 num_frame: 1,
1796 send_interval: Duration::from_millis(0),
1797 disconnect_delay: Duration::from_secs(30),
1798 },
1799 "resend" => SocketSchedule {
1800 num_frame: 3,
1801 send_interval: Duration::from_millis(100),
1802 disconnect_delay: Duration::from_secs(30),
1803 },
1804 "dump" => SocketSchedule {
1805 num_frame: 0,
1806 send_interval: Duration::from_millis(0),
1807 disconnect_delay: Duration::from_secs(30),
1808 },
1809 _ => {
1810 return Err("Failed to parse schedule".into());
1811 }
1812 };
1813
1814 self.context.lock().unwrap().socket_test_schedule = Some(schedule);
1815 }
1816 "send-msc" => {
1817 let dlci =
1818 String::from(get_arg(args, 1)?).parse::<u8>().or(Err("Failed parsing DLCI"))?;
1819 let addr = RawAddress::from_string(get_arg(args, 2)?).ok_or("Invalid Address")?;
1820 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().rfcomm_send_msc(dlci, addr);
1821 }
1822 "listen-rfcomm" => {
1823 let scn = String::from(get_arg(args, 1)?)
1824 .parse::<i32>()
1825 .or(Err("Failed parsing Service Channel Number"))?;
1826 let SocketResult { status, id } = self
1827 .context
1828 .lock()
1829 .unwrap()
1830 .socket_manager_dbus
1831 .as_mut()
1832 .unwrap()
1833 .listen_using_rfcomm(callback_id, Some(scn), None, None, None);
1834 if status != BtStatus::Success {
1835 return Err(format!(
1836 "Failed to request for listening using rfcomm, status = {:?}",
1837 status,
1838 )
1839 .into());
1840 }
1841 print_info!("Requested for listening using rfcomm on socket {}", id);
1842 }
1843 "listen" => {
1844 let auth_required = String::from(get_arg(args, 1)?)
1845 .parse::<bool>()
1846 .or(Err("Failed to parse auth-required"))?;
1847 let is_le = match &get_arg(args, 2)?[..] {
1848 "LE" => true,
1849 "Bredr" => false,
1850 _ => {
1851 return Err("Failed to parse socket type".into());
1852 }
1853 };
1854
1855 let SocketResult { status, id } = {
1856 let mut context_proxy = self.context.lock().unwrap();
1857 let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1858 if auth_required {
1859 if is_le {
1860 proxy.listen_using_l2cap_le_channel(callback_id)
1861 } else {
1862 proxy.listen_using_l2cap_channel(callback_id)
1863 }
1864 } else if is_le {
1865 proxy.listen_using_insecure_l2cap_le_channel(callback_id)
1866 } else {
1867 proxy.listen_using_insecure_l2cap_channel(callback_id)
1868 }
1869 };
1870
1871 if status != BtStatus::Success {
1872 return Err(format!(
1873 "Failed to request for listening using l2cap channel, status = {:?}",
1874 status,
1875 )
1876 .into());
1877 }
1878 print_info!("Requested for listening using l2cap channel on socket {}", id);
1879 }
1880 "connect" => {
1881 let (addr, sock_type, psm_or_uuid) =
1882 (&get_arg(args, 1)?, &get_arg(args, 2)?, &get_arg(args, 3)?);
1883 let device = BluetoothDevice {
1884 address: RawAddress::from_string(*addr).ok_or("Invalid Address")?,
1885 name: String::from("Socket Connect Device"),
1886 };
1887
1888 let auth_required = String::from(get_arg(args, 4)?)
1889 .parse::<bool>()
1890 .or(Err("Failed to parse auth-required"))?;
1891
1892 let is_le = match &get_arg(args, 5)?[..] {
1893 "LE" => true,
1894 "Bredr" => false,
1895 _ => {
1896 return Err("Failed to parse socket type".into());
1897 }
1898 };
1899
1900 let SocketResult { status, id } = {
1901 let mut context_proxy = self.context.lock().unwrap();
1902 let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap();
1903
1904 match &sock_type[0..] {
1905 "l2cap" => {
1906 let psm = match psm_or_uuid.parse::<i32>() {
1907 Ok(v) => v,
1908 Err(e) => {
1909 return Err(CommandError::Failed(format!(
1910 "Bad PSM given. Error={}",
1911 e
1912 )));
1913 }
1914 };
1915
1916 if auth_required {
1917 if is_le {
1918 proxy.create_l2cap_le_channel(callback_id, device, psm)
1919 } else {
1920 proxy.create_l2cap_channel(callback_id, device, psm)
1921 }
1922 } else if is_le {
1923 proxy.create_insecure_l2cap_le_channel(callback_id, device, psm)
1924 } else {
1925 proxy.create_insecure_l2cap_channel(callback_id, device, psm)
1926 }
1927 }
1928 "rfcomm" => {
1929 let uuid = match Uuid::from_string(*psm_or_uuid) {
1930 Some(uu) => uu,
1931 None => {
1932 return Err(CommandError::Failed(
1933 "Could not parse given uuid.".to_string(),
1934 ));
1935 }
1936 };
1937
1938 if auth_required {
1939 proxy.create_rfcomm_socket_to_service_record(
1940 callback_id,
1941 device,
1942 uuid,
1943 )
1944 } else {
1945 proxy.create_insecure_rfcomm_socket_to_service_record(
1946 callback_id,
1947 device,
1948 uuid,
1949 )
1950 }
1951 }
1952 _ => {
1953 return Err(CommandError::Failed(format!(
1954 "Unknown socket type: {}",
1955 sock_type
1956 )));
1957 }
1958 }
1959 };
1960
1961 if status != BtStatus::Success {
1962 return Err(CommandError::Failed(format!("Failed to create socket with status={:?} against {}, type {}, with psm/uuid {}",
1963 status, addr, sock_type, psm_or_uuid)));
1964 } else {
1965 print_info!("Called create socket with result ({:?}, {}) against {}, type {}, with psm/uuid {}",
1966 status, id, addr, sock_type, psm_or_uuid);
1967 }
1968 }
1969 "close" => {
1970 let sockid = String::from(get_arg(args, 1)?)
1971 .parse::<u64>()
1972 .or(Err("Failed parsing socket ID"))?;
1973 let status = self
1974 .context
1975 .lock()
1976 .unwrap()
1977 .socket_manager_dbus
1978 .as_mut()
1979 .unwrap()
1980 .close(callback_id, sockid);
1981 if status != BtStatus::Success {
1982 return Err(format!(
1983 "Failed to close the listening socket, status = {:?}",
1984 status,
1985 )
1986 .into());
1987 }
1988 }
1989
1990 _ => return Err(CommandError::InvalidArgs),
1991 };
1992
1993 Ok(())
1994 }
1995
cmd_hid(&mut self, args: &[String]) -> CommandResult1996 fn cmd_hid(&mut self, args: &[String]) -> CommandResult {
1997 if !self.context.lock().unwrap().adapter_ready {
1998 return Err(self.adapter_not_ready());
1999 }
2000
2001 let command = get_arg(args, 0)?;
2002
2003 match &command[..] {
2004 "get-report" => {
2005 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2006 let report_type = match &get_arg(args, 2)?[..] {
2007 "Input" => BthhReportType::InputReport,
2008 "Output" => BthhReportType::OutputReport,
2009 "Feature" => BthhReportType::FeatureReport,
2010 _ => {
2011 return Err("Failed to parse report type".into());
2012 }
2013 };
2014 let report_id = String::from(get_arg(args, 3)?)
2015 .parse::<u8>()
2016 .or(Err("Failed parsing report_id"))?;
2017
2018 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().get_hid_report(
2019 addr,
2020 report_type,
2021 report_id,
2022 );
2023 }
2024 "set-report" => {
2025 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2026 let report_type = match &get_arg(args, 2)?[..] {
2027 "Input" => BthhReportType::InputReport,
2028 "Output" => BthhReportType::OutputReport,
2029 "Feature" => BthhReportType::FeatureReport,
2030 _ => {
2031 return Err("Failed to parse report type".into());
2032 }
2033 };
2034 let report_value = String::from(get_arg(args, 3)?);
2035
2036 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().set_hid_report(
2037 addr,
2038 report_type,
2039 report_value,
2040 );
2041 }
2042 "send-data" => {
2043 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2044 let data = String::from(get_arg(args, 2)?);
2045
2046 self.context.lock().unwrap().qa_dbus.as_mut().unwrap().send_hid_data(addr, data);
2047 }
2048 "virtual-unplug" => {
2049 let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?;
2050 self.context
2051 .lock()
2052 .unwrap()
2053 .qa_dbus
2054 .as_mut()
2055 .unwrap()
2056 .send_hid_virtual_unplug(addr);
2057 }
2058 _ => return Err(CommandError::InvalidArgs),
2059 };
2060
2061 Ok(())
2062 }
2063
2064 /// Get the list of rules of supported commands
get_command_rule_list(&self) -> Vec<String>2065 pub fn get_command_rule_list(&self) -> Vec<String> {
2066 self.command_options.values().flat_map(|cmd| cmd.rules.clone()).collect()
2067 }
2068
cmd_list_devices(&mut self, args: &[String]) -> CommandResult2069 fn cmd_list_devices(&mut self, args: &[String]) -> CommandResult {
2070 if !self.lock_context().adapter_ready {
2071 return Err(self.adapter_not_ready());
2072 }
2073
2074 let command = get_arg(args, 0)?;
2075
2076 match &command[..] {
2077 "bonded" => {
2078 print_info!("Known bonded devices:");
2079 let devices =
2080 self.lock_context().adapter_dbus.as_ref().unwrap().get_bonded_devices();
2081 for device in devices.iter() {
2082 print_info!("[{}] {}", device.address.to_string(), device.name);
2083 }
2084 }
2085 "found" => {
2086 print_info!("Devices found in most recent discovery session:");
2087 for (key, val) in self.lock_context().found_devices.iter() {
2088 print_info!("[{:17}] {}", key, val.name);
2089 }
2090 }
2091 "connected" => {
2092 print_info!("Connected devices:");
2093 let devices =
2094 self.lock_context().adapter_dbus.as_ref().unwrap().get_connected_devices();
2095 for device in devices.iter() {
2096 print_info!("[{}] {}", device.address.to_string(), device.name);
2097 }
2098 }
2099 other => {
2100 println!("Invalid argument '{}'", other);
2101 }
2102 }
2103
2104 Ok(())
2105 }
2106
cmd_telephony(&mut self, args: &[String]) -> CommandResult2107 fn cmd_telephony(&mut self, args: &[String]) -> CommandResult {
2108 if !self.context.lock().unwrap().adapter_ready {
2109 return Err(self.adapter_not_ready());
2110 }
2111
2112 match &get_arg(args, 0)?[..] {
2113 "set-network" => {
2114 self.context
2115 .lock()
2116 .unwrap()
2117 .telephony_dbus
2118 .as_mut()
2119 .unwrap()
2120 .set_network_available(match &get_arg(args, 1)?[..] {
2121 "on" => true,
2122 "off" => false,
2123 other => {
2124 return Err(format!("Invalid argument '{}'", other).into());
2125 }
2126 });
2127 }
2128 "set-roaming" => {
2129 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().set_roaming(
2130 match &get_arg(args, 1)?[..] {
2131 "on" => true,
2132 "off" => false,
2133 other => {
2134 return Err(format!("Invalid argument '{}'", other).into());
2135 }
2136 },
2137 );
2138 }
2139 "set-signal" => {
2140 let strength = String::from(get_arg(args, 1)?)
2141 .parse::<i32>()
2142 .or(Err("Failed parsing signal strength"))?;
2143 if !(0..=5).contains(&strength) {
2144 return Err(
2145 format!("Invalid signal strength, got {}, want 0 to 5", strength).into()
2146 );
2147 }
2148 self.context
2149 .lock()
2150 .unwrap()
2151 .telephony_dbus
2152 .as_mut()
2153 .unwrap()
2154 .set_signal_strength(strength);
2155 }
2156 "set-battery" => {
2157 let level = String::from(get_arg(args, 1)?)
2158 .parse::<i32>()
2159 .or(Err("Failed parsing battery level"))?;
2160 if !(0..=5).contains(&level) {
2161 return Err(format!("Invalid battery level, got {}, want 0 to 5", level).into());
2162 }
2163 self.context
2164 .lock()
2165 .unwrap()
2166 .telephony_dbus
2167 .as_mut()
2168 .unwrap()
2169 .set_battery_level(level);
2170 }
2171 "enable" => {
2172 let mut context = self.lock_context();
2173 context.telephony_dbus.as_mut().unwrap().set_mps_qualification_enabled(true);
2174 if context.mps_sdp_handle.is_none() {
2175 let success = context
2176 .adapter_dbus
2177 .as_mut()
2178 .unwrap()
2179 .create_sdp_record(BtSdpRecord::Mps(BtSdpMpsRecord::default()));
2180 if !success {
2181 return Err("Failed to create SDP record".to_string().into());
2182 }
2183 }
2184 }
2185 "disable" => {
2186 let mut context = self.lock_context();
2187 context.telephony_dbus.as_mut().unwrap().set_mps_qualification_enabled(false);
2188 if let Some(handle) = context.mps_sdp_handle.take() {
2189 let success = context.adapter_dbus.as_mut().unwrap().remove_sdp_record(handle);
2190 if !success {
2191 return Err("Failed to remove SDP record".to_string().into());
2192 }
2193 }
2194 }
2195 "set-phone-ops" => {
2196 let on_or_off = match &get_arg(args, 1)?[..] {
2197 "on" => true,
2198 "off" => false,
2199 _ => {
2200 return Err("Failed to parse on|off".into());
2201 }
2202 };
2203 self.context
2204 .lock()
2205 .unwrap()
2206 .telephony_dbus
2207 .as_mut()
2208 .unwrap()
2209 .set_phone_ops_enabled(on_or_off);
2210 }
2211 "incoming-call" => {
2212 let success = self
2213 .context
2214 .lock()
2215 .unwrap()
2216 .telephony_dbus
2217 .as_mut()
2218 .unwrap()
2219 .incoming_call(String::from(get_arg(args, 1)?));
2220 if !success {
2221 return Err("IncomingCall failed".into());
2222 }
2223 }
2224 "dialing-call" => {
2225 let success = self
2226 .context
2227 .lock()
2228 .unwrap()
2229 .telephony_dbus
2230 .as_mut()
2231 .unwrap()
2232 .dialing_call(String::from(get_arg(args, 1)?));
2233 if !success {
2234 return Err("DialingCall failed".into());
2235 }
2236 }
2237 "answer-call" => {
2238 let success =
2239 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().answer_call();
2240 if !success {
2241 return Err("AnswerCall failed".into());
2242 }
2243 }
2244 "hangup-call" => {
2245 let success =
2246 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().hangup_call();
2247 if !success {
2248 return Err("HangupCall failed".into());
2249 }
2250 }
2251 "set-memory-call" => {
2252 let success = self
2253 .context
2254 .lock()
2255 .unwrap()
2256 .telephony_dbus
2257 .as_mut()
2258 .unwrap()
2259 .set_memory_call(get_arg(args, 1).ok().map(String::from));
2260 if !success {
2261 return Err("SetMemoryCall failed".into());
2262 }
2263 }
2264 "set-last-call" => {
2265 let success = self
2266 .context
2267 .lock()
2268 .unwrap()
2269 .telephony_dbus
2270 .as_mut()
2271 .unwrap()
2272 .set_last_call(get_arg(args, 1).ok().map(String::from));
2273 if !success {
2274 return Err("SetLastCall failed".into());
2275 }
2276 }
2277 "release-held" => {
2278 let success =
2279 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().release_held();
2280 if !success {
2281 return Err("ReleaseHeld failed".into());
2282 }
2283 }
2284 "release-active-accept-held" => {
2285 let success = self
2286 .context
2287 .lock()
2288 .unwrap()
2289 .telephony_dbus
2290 .as_mut()
2291 .unwrap()
2292 .release_active_accept_held();
2293 if !success {
2294 return Err("ReleaseActiveAcceptHeld failed".into());
2295 }
2296 }
2297 "hold-active-accept-held" => {
2298 let success = self
2299 .context
2300 .lock()
2301 .unwrap()
2302 .telephony_dbus
2303 .as_mut()
2304 .unwrap()
2305 .hold_active_accept_held();
2306 if !success {
2307 return Err("HoldActiveAcceptHeld failed".into());
2308 }
2309 }
2310 "audio-connect" => {
2311 let success =
2312 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().audio_connect(
2313 RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
2314 );
2315 if !success {
2316 return Err("ConnectAudio failed".into());
2317 }
2318 }
2319 "audio-disconnect" => {
2320 self.context.lock().unwrap().telephony_dbus.as_mut().unwrap().audio_disconnect(
2321 RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?,
2322 );
2323 }
2324 other => {
2325 return Err(format!("Invalid argument '{}'", other).into());
2326 }
2327 }
2328 Ok(())
2329 }
2330
cmd_qa(&mut self, args: &[String]) -> CommandResult2331 fn cmd_qa(&mut self, args: &[String]) -> CommandResult {
2332 if !self.context.lock().unwrap().adapter_ready {
2333 return Err(self.adapter_not_ready());
2334 }
2335
2336 let command = get_arg(args, 0)?;
2337
2338 match &command[..] {
2339 "add-media-player" => {
2340 let name = String::from(get_arg(args, 1)?);
2341 let browsing_supported = String::from(get_arg(args, 2)?)
2342 .parse::<bool>()
2343 .or(Err("Failed to parse browsing_supported"))?;
2344 self.context
2345 .lock()
2346 .unwrap()
2347 .qa_dbus
2348 .as_mut()
2349 .unwrap()
2350 .add_media_player(name, browsing_supported);
2351 }
2352 _ => return Err(CommandError::InvalidArgs),
2353 };
2354
2355 Ok(())
2356 }
2357
cmd_media(&mut self, args: &[String]) -> CommandResult2358 fn cmd_media(&mut self, args: &[String]) -> CommandResult {
2359 if !self.context.lock().unwrap().adapter_ready {
2360 return Err(self.adapter_not_ready());
2361 }
2362
2363 match &get_arg(args, 0)?[..] {
2364 "log" => {
2365 self.context.lock().unwrap().media_dbus.as_mut().unwrap().trigger_debug_dump();
2366 }
2367 other => {
2368 return Err(format!("Invalid argument '{}'", other).into());
2369 }
2370 }
2371
2372 Ok(())
2373 }
2374
cmd_dumpsys(&mut self, _args: &[String]) -> CommandResult2375 fn cmd_dumpsys(&mut self, _args: &[String]) -> CommandResult {
2376 if !self.lock_context().adapter_ready {
2377 return Err(self.adapter_not_ready());
2378 }
2379
2380 let contents = self.lock_context().adapter_dbus.as_mut().unwrap().get_dumpsys();
2381 println!("{}", contents);
2382
2383 Ok(())
2384 }
2385
cmd_log(&mut self, args: &[String]) -> CommandResult2386 fn cmd_log(&mut self, args: &[String]) -> CommandResult {
2387 if !self.lock_context().adapter_ready {
2388 return Err(self.adapter_not_ready());
2389 }
2390
2391 let command = get_arg(args, 0)?;
2392
2393 match &command[..] {
2394 "set-level" => {
2395 let level = match &get_arg(args, 1)?[..] {
2396 "info" => Level::Info,
2397 "debug" => Level::Debug,
2398 "verbose" => Level::Verbose,
2399 _ => {
2400 return Err("Failed to parse log level".into());
2401 }
2402 };
2403 self.lock_context().logging_dbus.as_mut().unwrap().set_log_level(level);
2404 }
2405
2406 "get-level" => {
2407 let level = self.lock_context().logging_dbus.as_ref().unwrap().get_log_level();
2408
2409 print_info!("log level: {:?}", level);
2410 }
2411
2412 other => {
2413 return Err(format!("Invalid argument '{}'", other).into());
2414 }
2415 }
2416
2417 Ok(())
2418 }
2419 }
2420
2421 #[cfg(test)]
2422 mod tests {
2423
2424 use super::*;
2425
2426 #[test]
test_wrap_help_text()2427 fn test_wrap_help_text() {
2428 let text = "hello";
2429 let text_len = text.chars().count();
2430 // ensure no overflow
2431 assert_eq!(format!("|{}|", text), wrap_help_text(text, 4, 0));
2432 assert_eq!(format!("|{}|", text), wrap_help_text(text, 5, 0));
2433 assert_eq!(format!("|{}{}|", text, " "), wrap_help_text(text, 6, 0));
2434 assert_eq!(format!("|{}{}|", text, " ".repeat(2)), wrap_help_text(text, 7, 0));
2435 assert_eq!(
2436 format!("|{}{}|", text, " ".repeat(100 - text_len)),
2437 wrap_help_text(text, 100, 0)
2438 );
2439 assert_eq!(format!("|{}{}|", " ", text), wrap_help_text(text, 4, 1));
2440 assert_eq!(format!("|{}{}|", " ".repeat(2), text), wrap_help_text(text, 5, 2));
2441 assert_eq!(format!("|{}{}{}|", " ".repeat(3), text, " "), wrap_help_text(text, 6, 3));
2442 assert_eq!(
2443 format!("|{}{}{}|", " ".repeat(4), text, " ".repeat(7 - text_len)),
2444 wrap_help_text(text, 7, 4)
2445 );
2446 assert_eq!(format!("|{}{}|", " ".repeat(9), text), wrap_help_text(text, 4, 9));
2447 assert_eq!(format!("|{}{}|", " ".repeat(10), text), wrap_help_text(text, 3, 10));
2448 assert_eq!(format!("|{}{}|", " ".repeat(11), text), wrap_help_text(text, 2, 11));
2449 assert_eq!(format!("|{}{}|", " ".repeat(12), text), wrap_help_text(text, 1, 12));
2450 assert_eq!("||", wrap_help_text("", 0, 0));
2451 assert_eq!("| |", wrap_help_text("", 1, 0));
2452 assert_eq!("| |", wrap_help_text("", 1, 1));
2453 assert_eq!("| |", wrap_help_text("", 0, 1));
2454 }
2455 }
2456