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,
         }
     }