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