diff --git a/system/gd/rust/linux/client/src/callbacks.rs b/system/gd/rust/linux/client/src/callbacks.rs index e0a30b3f1fe9507f2fa1ef26fb46c2bf825b8ab7..e32f98ab8b55b8aaedcb64a3ed44ae20efd3e3a6 100644 --- a/system/gd/rust/linux/client/src/callbacks.rs +++ b/system/gd/rust/linux/client/src/callbacks.rs @@ -1,3 +1,4 @@ +use crate::command_handler::SocketSchedule; use crate::dbus_iface::{ export_admin_policy_callback_dbus_intf, export_advertising_set_callback_dbus_intf, export_bluetooth_callback_dbus_intf, export_bluetooth_connection_callback_dbus_intf, @@ -29,7 +30,11 @@ use dbus::nonblock::SyncConnection; use dbus_crossroads::Crossroads; use dbus_projection::DisconnectWatcher; use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback; +use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; +use std::time::Duration; + +const SOCKET_TEST_WRITE: &[u8] = b"01234567890123456789"; /// Callback context for manager interface callbacks. pub(crate) struct BtManagerCallback { @@ -962,7 +967,6 @@ impl RPCProxy for BtGattServerCallback { pub(crate) struct BtSocketManagerCallback { objpath: String, context: Arc<Mutex<ClientContext>>, - dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, } @@ -976,6 +980,44 @@ impl BtSocketManagerCallback { ) -> Self { Self { objpath, context, dbus_connection, dbus_crossroads } } + + fn start_socket_schedule(&mut self, socket: BluetoothSocket) { + let SocketSchedule { num_frame, send_interval, disconnect_delay } = + match self.context.lock().unwrap().socket_test_schedule { + Some(s) => s, + None => return, + }; + + let mut fd = match socket.fd { + Some(fd) => fd, + None => { + print_error!("incoming connection fd is None. Unable to send data"); + return; + } + }; + + tokio::spawn(async move { + for i in 0..num_frame { + fd.write_all(SOCKET_TEST_WRITE); + print_info!("data sent: {}", i + 1); + tokio::time::sleep(send_interval).await; + } + + // dump any incoming data + let interval = 100; + for _d in (0..=disconnect_delay.as_millis()).step_by(interval) { + let mut buf = [0; 128]; + let sz = fd.read(&mut buf).unwrap(); + let data = buf[..sz].to_vec(); + if sz > 0 { + print_info!("received {} bytes: {:?}", sz, data); + } + tokio::time::sleep(Duration::from_millis(interval as u64)).await; + } + + //|fd| is dropped automatically when the scope ends. + }); + } } impl IBluetoothSocketManagerCallbacks for BtSocketManagerCallback { @@ -1005,18 +1047,16 @@ impl IBluetoothSocketManagerCallbacks for BtSocketManagerCallback { let callback_id = self.context.lock().unwrap().socket_manager_callback_id.clone().unwrap(); self.context.lock().unwrap().run_callback(Box::new(move |context| { - let status = context - .lock() - .unwrap() - .socket_manager_dbus - .as_mut() - .unwrap() - .close(callback_id, socket.id); + let status = context.lock().unwrap().socket_manager_dbus.as_mut().unwrap().accept( + callback_id, + socket.id, + None, + ); if status != BtStatus::Success { - print_error!("Failed to close socket {}, status = {:?}", socket.id, status); + print_error!("Failed to accept socket {}, status = {:?}", socket.id, status); return; } - print_info!("Requested for closing socket {}", socket.id); + print_info!("Requested for accepting socket {}", socket.id); })); } @@ -1026,10 +1066,11 @@ impl IBluetoothSocketManagerCallbacks for BtSocketManagerCallback { fn on_handle_incoming_connection( &mut self, - _listener_id: SocketId, - _connection: BluetoothSocket, + listener_id: SocketId, + connection: BluetoothSocket, ) { - todo!(); + print_info!("Socket {} connected", listener_id); + self.start_socket_schedule(connection); } fn on_outgoing_connection_result( @@ -1040,6 +1081,7 @@ impl IBluetoothSocketManagerCallbacks for BtSocketManagerCallback { ) { if let Some(s) = socket { print_info!("Connection success on {}: {:?} for {}", connecting_id, result, s); + self.start_socket_schedule(s); } else { print_info!("Connection failed on {}: {:?}", connecting_id, result); } diff --git a/system/gd/rust/linux/client/src/command_handler.rs b/system/gd/rust/linux/client/src/command_handler.rs index 52c8330c42fe1cdef83cfc12fa7cad79ef78cb79..a6818bd1654fcecf9c8d78eb0d384c10055bf12f 100644 --- a/system/gd/rust/linux/client/src/command_handler.rs +++ b/system/gd/rust/linux/client/src/command_handler.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::slice::SliceIndex; use std::sync::{Arc, Mutex}; +use std::time::Duration; use crate::bt_adv::AdvSet; use crate::bt_gatt::AuthReq; @@ -65,6 +66,21 @@ pub(crate) struct CommandHandler { command_options: HashMap<String, CommandOption>, } +/// Define what to do when a socket connects. Mainly for qualification purposes. +/// Specifically, after a socket is connected/accepted, we will do +/// (1) send a chunk of data every |send_interval| time until |num_frame| chunks has been sent. +/// (2) wait another |disconnect_delay| time. any incoming data will be dumpted during this time. +/// (3) disconnect the socket. +#[derive(Copy, Clone)] +pub struct SocketSchedule { + /// Number of times to send data + pub num_frame: u32, + /// Time interval between each sending + pub send_interval: Duration, + /// Extra time after the last sending. Any incoming data will be printed during this time. + pub disconnect_delay: Duration, +} + struct DisplayList<T>(Vec<T>); impl<T: Display> Display for DisplayList<T> { @@ -208,8 +224,10 @@ fn build_commands() -> HashMap<String, CommandOption> { String::from("socket"), CommandOption { rules: vec![ - String::from("socket test"), - String::from("socket connect <addr> <l2cap|rfcomm> <psm|uuid>"), + String::from("socket listen <auth-required>"), + String::from("socket connect <address> <l2cap|rfcomm> <psm|uuid> <auth-required>"), + String::from("socket disconnect <socket_id>"), + String::from("socket set-on-connect-schedule <send|resend|dump>"), ], description: String::from("Socket manager utilities."), function_pointer: CommandHandler::cmd_socket, @@ -1252,13 +1270,44 @@ impl CommandHandler { let command = get_arg(args, 0)?; match &command[..] { - "test" => { - let SocketResult { status, id } = self - .lock_context() - .socket_manager_dbus - .as_mut() - .unwrap() - .listen_using_l2cap_channel(callback_id); + "set-on-connect-schedule" => { + let schedule = match &get_arg(args, 1)?[..] { + "send" => SocketSchedule { + num_frame: 1, + send_interval: Duration::from_millis(0), + disconnect_delay: Duration::from_secs(30), + }, + "resend" => SocketSchedule { + num_frame: 3, + send_interval: Duration::from_millis(100), + disconnect_delay: Duration::from_secs(30), + }, + "dump" => SocketSchedule { + num_frame: 0, + send_interval: Duration::from_millis(0), + disconnect_delay: Duration::from_secs(30), + }, + _ => { + return Err("Failed to parse schedule".into()); + } + }; + + self.context.lock().unwrap().socket_test_schedule = Some(schedule); + } + "listen" => { + let auth_required = String::from(get_arg(args, 1)?) + .parse::<bool>() + .or(Err("Failed to parse auth-required"))?; + + let SocketResult { status, id } = { + let mut context_proxy = self.context.lock().unwrap(); + let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap(); + if auth_required { + proxy.listen_using_l2cap_channel(callback_id) + } else { + proxy.listen_using_insecure_l2cap_channel(callback_id) + } + }; if status != BtStatus::Success { return Err(format!( @@ -1277,49 +1326,62 @@ impl CommandHandler { name: String::from("Socket Connect Device"), }; - let SocketResult { status, id } = match &sock_type[0..] { - "l2cap" => { - let psm = match psm_or_uuid.clone().parse::<i32>() { - Ok(v) => v, - Err(e) => { - return Err(CommandError::Failed(format!( - "Bad PSM given. Error={}", - e - ))); + let auth_required = String::from(get_arg(args, 4)?) + .parse::<bool>() + .or(Err("Failed to parse auth-required"))?; + + let SocketResult { status, id } = { + let mut context_proxy = self.context.lock().unwrap(); + let proxy = context_proxy.socket_manager_dbus.as_mut().unwrap(); + + match &sock_type[0..] { + "l2cap" => { + let psm = match psm_or_uuid.clone().parse::<i32>() { + Ok(v) => v, + Err(e) => { + return Err(CommandError::Failed(format!( + "Bad PSM given. Error={}", + e + ))); + } + }; + + if auth_required { + proxy.create_l2cap_channel(callback_id, device, psm) + } else { + proxy.create_insecure_l2cap_channel(callback_id, device, psm) } - }; - - self.lock_context() - .socket_manager_dbus - .as_mut() - .unwrap() - .create_insecure_l2cap_channel(callback_id, device, psm) - } - "rfcomm" => { - let uuid = match UuidHelper::parse_string(psm_or_uuid.clone()) { - Some(uu) => uu, - None => { - return Err(CommandError::Failed(format!( - "Could not parse given uuid." - ))); + } + "rfcomm" => { + let uuid = match UuidHelper::parse_string(psm_or_uuid.clone()) { + Some(uu) => uu, + None => { + return Err(CommandError::Failed(format!( + "Could not parse given uuid." + ))); + } + }; + + if auth_required { + proxy.create_rfcomm_socket_to_service_record( + callback_id, + device, + uuid, + ) + } else { + proxy.create_insecure_rfcomm_socket_to_service_record( + callback_id, + device, + uuid, + ) } - }; - - self.lock_context() - .socket_manager_dbus - .as_mut() - .unwrap() - .create_insecure_rfcomm_socket_to_service_record( - callback_id, - device, - uuid, - ) - } - _ => { - return Err(CommandError::Failed(format!( - "Unknown socket type: {}", - sock_type - ))); + } + _ => { + return Err(CommandError::Failed(format!( + "Unknown socket type: {}", + sock_type + ))); + } } }; diff --git a/system/gd/rust/linux/client/src/main.rs b/system/gd/rust/linux/client/src/main.rs index 54a93d55f4aa6746084b484e27170b3abe6d1751..785295cd8fefadcf844bdd8c5ddf7fb273419469 100644 --- a/system/gd/rust/linux/client/src/main.rs +++ b/system/gd/rust/linux/client/src/main.rs @@ -15,7 +15,7 @@ use crate::callbacks::{ AdminCallback, AdvertisingSetCallback, BtCallback, BtConnectionCallback, BtManagerCallback, BtSocketManagerCallback, ScannerCallback, SuspendCallback, }; -use crate::command_handler::CommandHandler; +use crate::command_handler::{CommandHandler, SocketSchedule}; use crate::dbus_iface::{ BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothQADBus, BluetoothSocketManagerDBus, SuspendDBus, @@ -121,6 +121,9 @@ pub(crate) struct ClientContext { /// Data of GATT client preference. gatt_client_context: GattClientContext, + + /// The schedule when a socket is connected. + socket_test_schedule: Option<SocketSchedule>, } impl ClientContext { @@ -162,6 +165,7 @@ impl ClientContext { socket_manager_callback_id: None, is_restricted, gatt_client_context: GattClientContext::new(), + socket_test_schedule: None, } }