From 5a7c270d08aeaffba34cfde5faa532f547b52b77 Mon Sep 17 00:00:00 2001 From: Christoph Lemannczick Date: Fri, 22 Aug 2025 19:29:21 +0200 Subject: [PATCH] support for `exec` --- adb_client/src/adb_device_ext.rs | 9 +++ .../src/device/adb_message_device_commands.rs | 9 +++ adb_client/src/device/adb_tcp_device.rs | 10 +++ adb_client/src/device/adb_usb_device.rs | 10 +++ adb_client/src/device/commands/shell.rs | 23 +++++- adb_client/src/models/adb_server_command.rs | 2 + .../adb_server_device_commands.rs | 70 ++++++++++++------- 7 files changed, 106 insertions(+), 27 deletions(-) diff --git a/adb_client/src/adb_device_ext.rs b/adb_client/src/adb_device_ext.rs index 9fc99a4a..0b594ecc 100644 --- a/adb_client/src/adb_device_ext.rs +++ b/adb_client/src/adb_device_ext.rs @@ -15,6 +15,15 @@ pub trait ADBDeviceExt { /// Input data is read from reader and write to writer. fn shell(&mut self, reader: &mut dyn Read, writer: Box<(dyn Write + Send)>) -> Result<()>; + /// Runs command on the device. + /// Input data is read from reader and write to writer. + fn exec( + &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()>; + /// Display the stat information for a remote file fn stat(&mut self, remote_path: &str) -> Result; diff --git a/adb_client/src/device/adb_message_device_commands.rs b/adb_client/src/device/adb_message_device_commands.rs index 78b9e020..2450d24d 100644 --- a/adb_client/src/device/adb_message_device_commands.rs +++ b/adb_client/src/device/adb_message_device_commands.rs @@ -15,6 +15,15 @@ impl ADBDeviceExt for ADBMessageDevice { self.shell(reader, writer) } + fn exec( + &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.exec(command, reader, writer) + } + fn stat(&mut self, remote_path: &str) -> Result { self.stat(remote_path) } diff --git a/adb_client/src/device/adb_tcp_device.rs b/adb_client/src/device/adb_tcp_device.rs index 5f2e8d3a..447c0e53 100644 --- a/adb_client/src/device/adb_tcp_device.rs +++ b/adb_client/src/device/adb_tcp_device.rs @@ -79,6 +79,16 @@ impl ADBDeviceExt for ADBTcpDevice { self.inner.shell(reader, writer) } + #[inline] + fn exec( + &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.inner.exec(command, reader, writer) + } + #[inline] fn stat(&mut self, remote_path: &str) -> Result { self.inner.stat(remote_path) diff --git a/adb_client/src/device/adb_usb_device.rs b/adb_client/src/device/adb_usb_device.rs index 1de4376e..8e6a35b2 100644 --- a/adb_client/src/device/adb_usb_device.rs +++ b/adb_client/src/device/adb_usb_device.rs @@ -264,6 +264,16 @@ impl ADBDeviceExt for ADBUSBDevice { self.inner.shell(reader, writer) } + #[inline] + fn exec( + &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.inner.exec(command, reader, writer) + } + #[inline] fn stat(&mut self, remote_path: &str) -> Result { self.inner.stat(remote_path) diff --git a/adb_client/src/device/commands/shell.rs b/adb_client/src/device/commands/shell.rs index eea70979..79a070f6 100644 --- a/adb_client/src/device/commands/shell.rs +++ b/adb_client/src/device/commands/shell.rs @@ -35,10 +35,31 @@ impl ADBMessageDevice { /// Input data is read from [reader] and write to [writer]. pub(crate) fn shell( &mut self, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.bidi_session(b"shell:\0", reader, writer) + } + + /// Runs `command` on the device. + /// Input data is read from [reader] and write to [writer]. + pub(crate) fn exec( + &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.bidi_session(format!("exec:{}\0", command).as_bytes(), reader, writer) + } + + /// Starts an bidirectional(interactive) session. This can be a shell or an exec session. + fn bidi_session( + &mut self, + session_data: &[u8], mut reader: &mut dyn Read, mut writer: Box<(dyn Write + Send)>, ) -> Result<()> { - self.open_session(b"shell:\0")?; + self.open_session(session_data)?; let mut transport = self.get_transport().clone(); diff --git a/adb_client/src/models/adb_server_command.rs b/adb_client/src/models/adb_server_command.rs index 8790fbc4..8c19a455 100644 --- a/adb_client/src/models/adb_server_command.rs +++ b/adb_client/src/models/adb_server_command.rs @@ -28,6 +28,7 @@ pub(crate) enum AdbServerCommand { // Local commands ShellCommand(String), Shell, + Exec(String), FrameBuffer, Sync, Reboot(RebootType), @@ -59,6 +60,7 @@ impl Display for AdbServerCommand { Ok(term) => write!(f, "shell,TERM={term},raw:"), Err(_) => write!(f, "shell,raw:"), }, + AdbServerCommand::Exec(command) => write!(f, "exec:{command}"), AdbServerCommand::HostFeatures => write!(f, "host:features"), AdbServerCommand::Reboot(reboot_type) => { write!(f, "reboot:{reboot_type}") diff --git a/adb_client/src/server_device/adb_server_device_commands.rs b/adb_client/src/server_device/adb_server_device_commands.rs index af276ed0..95a584ca 100644 --- a/adb_client/src/server_device/adb_server_device_commands.rs +++ b/adb_client/src/server_device/adb_server_device_commands.rs @@ -46,11 +46,53 @@ impl ADBDeviceExt for ADBServerDevice { self.stat(remote_path) } - fn shell( + fn exec( &mut self, + command: &str, + reader: &mut dyn Read, + writer: Box<(dyn Write + Send)>, + ) -> Result<()> { + self.bidi_session(AdbServerCommand::Exec(command.to_owned()), reader, writer) + } + + fn shell(&mut self, reader: &mut dyn Read, writer: Box<(dyn Write + Send)>) -> Result<()> { + self.bidi_session(AdbServerCommand::Shell, reader, writer) + } + + fn pull(&mut self, source: &dyn AsRef, mut output: &mut dyn Write) -> Result<()> { + self.pull(source, &mut output) + } + + fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { + self.reboot(reboot_type) + } + + fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { + self.push(stream, path) + } + + fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { + self.install(apk_path) + } + + fn uninstall(&mut self, package: &str) -> Result<()> { + self.uninstall(package) + } + + fn framebuffer_inner(&mut self) -> Result, Vec>> { + self.framebuffer_inner() + } +} + +impl ADBServerDevice { + fn bidi_session( + &mut self, + server_cmd: AdbServerCommand, mut reader: &mut dyn Read, mut writer: Box<(dyn Write + Send)>, ) -> Result<()> { + // TODO: Not sure if this feature check is neccecery if server_cmd is `AdbServerCommand::Exec(_)`. + // If it isn't move this check to `::shell`. let supported_features = self.host_features()?; if !supported_features.contains(&HostFeatures::ShellV2) && !supported_features.contains(&HostFeatures::Cmd) @@ -59,7 +101,7 @@ impl ADBDeviceExt for ADBServerDevice { } self.set_serial_transport()?; - self.transport.send_adb_request(AdbServerCommand::Shell)?; + self.transport.send_adb_request(server_cmd)?; let mut read_stream = self.transport.get_raw_connection()?.try_clone()?; @@ -95,28 +137,4 @@ impl ADBDeviceExt for ADBServerDevice { Ok(()) } - - fn pull(&mut self, source: &dyn AsRef, mut output: &mut dyn Write) -> Result<()> { - self.pull(source, &mut output) - } - - fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { - self.reboot(reboot_type) - } - - fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { - self.push(stream, path) - } - - fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { - self.install(apk_path) - } - - fn uninstall(&mut self, package: &str) -> Result<()> { - self.uninstall(package) - } - - fn framebuffer_inner(&mut self) -> Result, Vec>> { - self.framebuffer_inner() - } }