use adb_client::{ADBDeviceExt, ADBServer};
use std::io;
use std::net::{Ipv4Addr, SocketAddrV4};
pub type AdbResult<T> = Result<T, AdbError>;
#[derive(Debug)]
pub enum AdbError {
ConnectionError(String),
DeviceNotFound,
CommandFailed(String),
IoError(io::Error),
ParseError(String),
NoDeviceSelected,
}
impl std::fmt::Display for AdbError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AdbError::ConnectionError(msg) => write!(f, "Connection error: {}", msg),
AdbError::DeviceNotFound => write!(f, "Device not found"),
AdbError::CommandFailed(msg) => write!(f, "Command failed: {}", msg),
AdbError::IoError(e) => write!(f, "IO error: {}", e),
AdbError::ParseError(msg) => write!(f, "Parse error: {}", msg),
AdbError::NoDeviceSelected => write!(f, "No device selected"),
}
}
}
impl std::error::Error for AdbError {}
impl From<io::Error> for AdbError {
fn from(e: io::Error) -> Self {
AdbError::IoError(e)
}
}
impl From<adb_client::RustADBError> for AdbError {
fn from(e: adb_client::RustADBError) -> Self {
AdbError::CommandFailed(e.to_string())
}
}
#[derive(Debug, Clone)]
pub enum AdbCommand {
ListDevices,
GetDeviceState,
GetSerialNumber,
ListPackages {
include_path: bool,
filter: PackageFilter,
},
GetPackageInfo {
package_name: String,
},
InstallPackage {
apk_path: String,
},
UninstallPackage {
package_name: String,
},
ClearPackageData {
package_name: String,
},
GetBatteryInfo,
GetMemoryInfo,
GetCpuInfo,
GetDeviceProperties,
GetSystemLog {
lines: usize,
},
GetNetworkInfo,
GetWifiStatus,
TakeScreenshot,
GetScreenResolution,
ListProcesses,
ForceStop {
package_name: String,
},
Shell {
command: String,
},
GetAdbVersion,
}
#[derive(Debug, Clone)]
pub enum PackageFilter {
All,
User, System, Enabled, Disabled, }
#[derive(Debug, Clone)]
pub struct DeviceInfo {
pub serial: String,
pub state: String,
pub model: Option<String>,
pub device: Option<String>,
}
#[derive(Debug)]
pub struct AdbManager {
server: Option<ADBServer>,
selected_device: Option<String>,
}
impl AdbManager {
pub fn new() -> Self {
Self {
server: None,
selected_device: None,
}
}
pub fn connect(&mut self) -> AdbResult<()> {
let socket_addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 5037);
let server = ADBServer::new(socket_addr);
self.server = Some(server);
Ok(())
}
fn get_server(&mut self) -> AdbResult<&mut ADBServer> {
if self.server.is_none() {
self.connect()?;
}
self.server
.as_mut()
.ok_or(AdbError::ConnectionError("Failed to connect".to_string()))
}
pub fn select_device(&mut self, serial: String) {
self.selected_device = Some(serial);
}
fn get_selected_device(&self) -> AdbResult<&str> {
self.selected_device
.as_deref()
.ok_or(AdbError::NoDeviceSelected)
}
pub fn execute(&mut self, command: AdbCommand) -> AdbResult<String> {
match command {
AdbCommand::ListDevices => self.list_devices(),
AdbCommand::GetDeviceState => self.get_device_state(),
AdbCommand::GetSerialNumber => self.get_serial_number(),
AdbCommand::ListPackages {
include_path,
filter,
} => self.list_packages(include_path, filter),
AdbCommand::GetPackageInfo { package_name } => self.get_package_info(&package_name),
AdbCommand::InstallPackage { apk_path } => self.install_package(&apk_path),
AdbCommand::UninstallPackage { package_name } => self.uninstall_package(&package_name),
AdbCommand::ClearPackageData { package_name } => self.clear_package_data(&package_name),
AdbCommand::GetBatteryInfo => self.get_battery_info(),
AdbCommand::GetMemoryInfo => self.get_memory_info(),
AdbCommand::GetCpuInfo => self.get_cpu_info(),
AdbCommand::GetDeviceProperties => self.get_device_properties(),
AdbCommand::GetSystemLog { lines } => self.get_system_log(lines),
AdbCommand::GetNetworkInfo => self.get_network_info(),
AdbCommand::GetWifiStatus => self.get_wifi_status(),
AdbCommand::TakeScreenshot => self.take_screenshot(),
AdbCommand::GetScreenResolution => self.get_screen_resolution(),
AdbCommand::ListProcesses => self.list_processes(),
AdbCommand::ForceStop { package_name } => self.force_stop(&package_name),
AdbCommand::Shell { command } => self.shell_command(&command),
AdbCommand::GetAdbVersion => self.get_adb_version(),
}
}
fn list_devices(&mut self) -> AdbResult<String> {
let server = self.get_server()?;
let devices = server.devices()?;
if devices.is_empty() {
return Ok("No devices found.\n\nMake sure:\n- Device is connected via USB\n- USB debugging is enabled\n- Device is authorized".to_string());
}
let mut output = String::from("List of devices attached:\n");
for device in devices {
output.push_str(&format!("{}\t{:?}\n", device.identifier, device.state));
if self.selected_device.is_none() {
self.selected_device = Some(device.identifier.clone());
}
}
Ok(output)
}
fn get_device_state(&mut self) -> AdbResult<String> {
let serial = self.get_selected_device()?.to_string();
let server = self.get_server()?;
let devices = server.devices()?;
for device in devices {
if device.identifier == serial {
return Ok(format!("Device state: {:?}", device.state));
}
}
Err(AdbError::DeviceNotFound)
}
fn get_serial_number(&mut self) -> AdbResult<String> {
let serial = self.get_selected_device()?;
Ok(format!("Serial number: {}", serial))
}
fn list_packages(&mut self, include_path: bool, filter: PackageFilter) -> AdbResult<String> {
let filter_arg = match filter {
PackageFilter::All => "",
PackageFilter::User => " -3",
PackageFilter::System => " -s",
PackageFilter::Enabled => " -e",
PackageFilter::Disabled => " -d",
};
let path_arg = if include_path { " -f" } else { "" };
let command = format!("pm list packages{}{}", path_arg, filter_arg);
self.shell_command(&command)
}
fn get_package_info(&mut self, package_name: &str) -> AdbResult<String> {
let command = format!("dumpsys package {}", package_name);
self.shell_command(&command)
}
fn install_package(&mut self, _apk_path: &str) -> AdbResult<String> {
Err(AdbError::CommandFailed(
"Install via adb_client not yet implemented. Use shell command.".to_string(),
))
}
fn uninstall_package(&mut self, package_name: &str) -> AdbResult<String> {
let command = format!("pm uninstall {}", package_name);
self.shell_command(&command)
}
fn clear_package_data(&mut self, package_name: &str) -> AdbResult<String> {
let command = format!("pm clear {}", package_name);
self.shell_command(&command)
}
fn get_battery_info(&mut self) -> AdbResult<String> {
self.shell_command("dumpsys battery")
}
fn get_memory_info(&mut self) -> AdbResult<String> {
self.shell_command("dumpsys meminfo")
}
fn get_cpu_info(&mut self) -> AdbResult<String> {
self.shell_command("cat /proc/cpuinfo")
}
fn get_device_properties(&mut self) -> AdbResult<String> {
self.shell_command("getprop")
}
fn get_system_log(&mut self, lines: usize) -> AdbResult<String> {
let command = format!("logcat -d -t {}", lines);
self.shell_command(&command)
}
fn get_network_info(&mut self) -> AdbResult<String> {
self.shell_command("dumpsys connectivity")
}
fn get_wifi_status(&mut self) -> AdbResult<String> {
self.shell_command("ip addr show wlan0")
}
fn take_screenshot(&mut self) -> AdbResult<String> {
self.shell_command("screencap -p /sdcard/screenshot.png")
}
fn get_screen_resolution(&mut self) -> AdbResult<String> {
let size = self.shell_command("wm size")?;
let density = self.shell_command("wm density")?;
Ok(format!("{}\n{}", size, density))
}
fn list_processes(&mut self) -> AdbResult<String> {
self.shell_command("ps")
}
fn force_stop(&mut self, package_name: &str) -> AdbResult<String> {
let command = format!("am force-stop {}", package_name);
self.shell_command(&command)
}
fn shell_command(&mut self, command: &str) -> AdbResult<String> {
let server = self.get_server()?;
let mut device = server
.get_device()
.map_err(|e| AdbError::ConnectionError(format!("Failed to get device: {}", e)))?;
let mut output = Vec::new();
device
.shell_command(&[command], &mut output)
.map_err(|e| AdbError::CommandFailed(format!("Shell command failed: {}", e)))?;
let result = String::from_utf8_lossy(&output).to_string();
if result.trim().is_empty() {
Ok("Command executed successfully (no output)".to_string())
} else {
Ok(result)
}
}
fn get_adb_version(&mut self) -> AdbResult<String> {
let server = self.get_server()?;
let version = server.version()?;
Ok(format!(
"ADB server version: {}.{}.{}",
version.major, version.minor, version.revision
))
}
}
impl Default for AdbManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adb_manager_creation() {
let manager = AdbManager::new();
assert!(manager.server.is_none());
assert!(manager.selected_device.is_none());
}
#[test]
fn test_package_filter() {
let filter = PackageFilter::User;
assert!(matches!(filter, PackageFilter::User));
}
#[test]
fn test_adb_command_creation() {
let cmd = AdbCommand::ListDevices;
assert!(matches!(cmd, AdbCommand::ListDevices));
}
#[test]
fn test_error_display() {
let error = AdbError::DeviceNotFound;
assert_eq!(error.to_string(), "Device not found");
}
}