From ad064a9f41b178ed3bf42891cd090ad7a186146d Mon Sep 17 00:00:00 2001 From: cli Date: Fri, 7 Mar 2025 17:01:26 +0100 Subject: [PATCH] feat: add wait-for-device command (#96) --- adb_cli/src/handlers/host_commands.rs | 6 +- adb_cli/src/models/host.rs | 13 ++++ adb_client/src/error.rs | 3 + adb_client/src/models/adb_server_command.rs | 9 +++ adb_client/src/server/commands/mod.rs | 1 + .../src/server/commands/wait_for_device.rs | 20 ++++++ adb_client/src/server/models/mod.rs | 2 + .../src/server/models/wait_for_device.rs | 67 +++++++++++++++++++ .../src/transports/tcp_server_transport.rs | 5 ++ 9 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 adb_client/src/server/commands/wait_for_device.rs create mode 100644 adb_client/src/server/models/wait_for_device.rs diff --git a/adb_cli/src/handlers/host_commands.rs b/adb_cli/src/handlers/host_commands.rs index 411bfa1..413ee1d 100644 --- a/adb_cli/src/handlers/host_commands.rs +++ b/adb_cli/src/handlers/host_commands.rs @@ -1,4 +1,4 @@ -use adb_client::{ADBServer, DeviceShort, MDNSBackend, Result}; +use adb_client::{ADBServer, DeviceShort, MDNSBackend, Result, WaitForDeviceState}; use crate::models::{HostCommand, MdnsCommand, ServerCommand}; @@ -72,6 +72,10 @@ pub fn handle_host_commands(server_command: ServerCommand) -> Resul HostCommand::ServerStatus => { log::info!("{}", adb_server.server_status()?); } + HostCommand::WaitForDevice { transport } => { + log::info!("waiting for device to be connected..."); + adb_server.wait_for_device(WaitForDeviceState::Device, transport)?; + } } Ok(()) diff --git a/adb_cli/src/models/host.rs b/adb_cli/src/models/host.rs index 21505bb..4161d18 100644 --- a/adb_cli/src/models/host.rs +++ b/adb_cli/src/models/host.rs @@ -1,7 +1,14 @@ use std::net::SocketAddrV4; +use adb_client::{RustADBError, WaitForDeviceTransport}; use clap::Parser; +fn parse_wait_for_device_device_transport( + value: &str, +) -> Result { + WaitForDeviceTransport::try_from(value) +} + #[derive(Parser, Debug)] pub enum HostCommand { /// Print current ADB version. @@ -28,6 +35,12 @@ pub enum HostCommand { }, /// Display server status ServerStatus, + /// Wait for a device, on optionally given transport + WaitForDevice { + /// Transport on which wait for devices + #[clap(short = 't', long = "transport", value_parser = parse_wait_for_device_device_transport)] + transport: Option, + }, } #[derive(Parser, Debug)] diff --git a/adb_client/src/error.rs b/adb_client/src/error.rs index 42dbdc1..0164211 100644 --- a/adb_client/src/error.rs +++ b/adb_client/src/error.rs @@ -117,6 +117,9 @@ pub enum RustADBError { /// An error occurred while sending data to channel #[error(transparent)] SendError(#[from] std::sync::mpsc::SendError), + /// An unknown transport has been provided + #[error("unknown transport: {0}")] + UnknownTransport(String), } impl From> for RustADBError { diff --git a/adb_client/src/models/adb_server_command.rs b/adb_client/src/models/adb_server_command.rs index 71fdd7d..a36e2ef 100644 --- a/adb_client/src/models/adb_server_command.rs +++ b/adb_client/src/models/adb_server_command.rs @@ -1,5 +1,7 @@ use std::fmt::Display; +use crate::{WaitForDeviceState, WaitForDeviceTransport}; + use super::RebootType; use std::net::SocketAddrV4; @@ -22,6 +24,7 @@ pub(crate) enum AdbServerCommand { ReconnectOffline, Uninstall(String), Install(u64), + WaitForDevice(WaitForDeviceState, WaitForDeviceTransport), // Local commands ShellCommand(String), Shell, @@ -87,6 +90,12 @@ impl Display for AdbServerCommand { AdbServerCommand::Uninstall(package) => { write!(f, "exec:cmd package 'uninstall' {package}") } + AdbServerCommand::WaitForDevice(wait_for_device_state, wait_for_device_transport) => { + write!( + f, + "host:wait-for-{wait_for_device_transport}-{wait_for_device_state}" + ) + } } } } diff --git a/adb_client/src/server/commands/mod.rs b/adb_client/src/server/commands/mod.rs index 3931b39..4a1b77f 100644 --- a/adb_client/src/server/commands/mod.rs +++ b/adb_client/src/server/commands/mod.rs @@ -7,3 +7,4 @@ mod pair; mod reconnect; mod server_status; mod version; +mod wait_for_device; diff --git a/adb_client/src/server/commands/wait_for_device.rs b/adb_client/src/server/commands/wait_for_device.rs new file mode 100644 index 0000000..7c47cdc --- /dev/null +++ b/adb_client/src/server/commands/wait_for_device.rs @@ -0,0 +1,20 @@ +use crate::{ + models::AdbServerCommand, ADBServer, Result, WaitForDeviceState, WaitForDeviceTransport, +}; + +impl ADBServer { + /// Wait for a device in a given state to be connected + pub fn wait_for_device( + &mut self, + state: WaitForDeviceState, + transport: Option, + ) -> Result<()> { + let transport = transport.unwrap_or_default(); + + self.connect()? + .send_adb_request(AdbServerCommand::WaitForDevice(state, transport))?; + + // Server should respond with an "OKAY" response + self.get_transport()?.read_adb_response() + } +} diff --git a/adb_client/src/server/models/mod.rs b/adb_client/src/server/models/mod.rs index 1bb8870..0cbdb8e 100644 --- a/adb_client/src/server/models/mod.rs +++ b/adb_client/src/server/models/mod.rs @@ -4,6 +4,7 @@ mod device_short; mod device_state; mod mdns_services; mod server_status; +mod wait_for_device; pub use adb_version::AdbVersion; pub use device_long::DeviceLong; @@ -11,3 +12,4 @@ pub use device_short::DeviceShort; pub use device_state::DeviceState; pub use mdns_services::MDNSServices; pub use server_status::{MDNSBackend, ServerStatus}; +pub use wait_for_device::{WaitForDeviceState, WaitForDeviceTransport}; diff --git a/adb_client/src/server/models/wait_for_device.rs b/adb_client/src/server/models/wait_for_device.rs new file mode 100644 index 0000000..76518ae --- /dev/null +++ b/adb_client/src/server/models/wait_for_device.rs @@ -0,0 +1,67 @@ +use std::fmt::Display; + +use crate::RustADBError; + +#[derive(Clone, Debug)] +/// List of available transports to wait for. +pub enum WaitForDeviceTransport { + /// USB transport + Usb, + /// Local transport + Local, + /// Any transport (default value) + Any, +} + +impl Default for WaitForDeviceTransport { + fn default() -> Self { + Self::Any + } +} + +impl Display for WaitForDeviceTransport { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WaitForDeviceTransport::Usb => write!(f, "usb"), + WaitForDeviceTransport::Local => write!(f, "local"), + WaitForDeviceTransport::Any => write!(f, "any"), + } + } +} + +impl TryFrom<&str> for WaitForDeviceTransport { + type Error = RustADBError; + + fn try_from(value: &str) -> Result { + match value { + "usb" => Ok(Self::Usb), + "local" => Ok(Self::Local), + "any" => Ok(Self::Any), + t => Err(RustADBError::UnknownTransport(t.to_string())), + } + } +} + +#[derive(Debug)] +/// List of available states to wait for. +pub enum WaitForDeviceState { + /// Device in "device" state + Device, + /// Device in "recovery" state + Recovery, + /// Device in "sideload" state + Sideload, + /// Device in "bootloader" state + Bootloader, +} + +impl Display for WaitForDeviceState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WaitForDeviceState::Device => write!(f, "device"), + WaitForDeviceState::Recovery => write!(f, "recovery"), + WaitForDeviceState::Sideload => write!(f, "sideload"), + WaitForDeviceState::Bootloader => write!(f, "bootloader"), + } + } +} diff --git a/adb_client/src/transports/tcp_server_transport.rs b/adb_client/src/transports/tcp_server_transport.rs index ded03d5..9200bfd 100644 --- a/adb_client/src/transports/tcp_server_transport.rs +++ b/adb_client/src/transports/tcp_server_transport.rs @@ -113,6 +113,11 @@ impl TCPServerTransport { self.get_raw_connection()? .write_all(adb_request.as_bytes())?; + self.read_adb_response() + } + + /// Read a response from ADB server + pub(crate) fn read_adb_response(&mut self) -> Result<()> { // Reads returned status code from ADB server let mut request_status = [0; 4]; self.get_raw_connection()?.read_exact(&mut request_status)?;