2 Commits

Author SHA1 Message Date
LIAUD Corentin
c0085ab93b fix: hex display for --list 2025-08-17 11:43:47 +02:00
LIAUD Corentin
66fa9679a4 feat: add find_all_connected_adb_devices function 2025-08-17 11:39:19 +02:00
6 changed files with 118 additions and 22 deletions

View File

@@ -16,6 +16,7 @@ anyhow = { version = "1.0.94" }
clap = { version = "4.5.23", features = ["derive"] }
env_logger = { version = "0.11.5" }
log = { version = "0.4.26" }
tabwriter = { version = "1.4.1" }
[target.'cfg(unix)'.dependencies]
termios = { version = "0.3.3" }

View File

@@ -8,20 +8,22 @@ mod models;
mod utils;
use adb_client::{
ADBDeviceExt, ADBServer, ADBServerDevice, ADBTcpDevice, ADBUSBDevice, MDNSDiscoveryService,
ADBDeviceExt, ADBDeviceInfo, ADBServer, ADBServerDevice, ADBTcpDevice, ADBUSBDevice,
MDNSDiscoveryService, find_all_connected_adb_devices,
};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use adb_termios::ADBTermios;
use anyhow::Result;
use anyhow::{Result, bail};
use clap::Parser;
use handlers::{handle_emulator_commands, handle_host_commands, handle_local_commands};
use models::{DeviceCommands, LocalCommand, MainCommand, Opts};
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::io::{Write, stdout};
use std::path::Path;
use tabwriter::TabWriter;
use utils::setup_logger;
fn main() -> Result<()> {
@@ -61,6 +63,34 @@ fn main() -> Result<()> {
}
}
MainCommand::Usb(usb_command) => {
if usb_command.list_devices {
let devices = find_all_connected_adb_devices()?;
let mut writer = TabWriter::new(stdout()).alignment(tabwriter::Alignment::Center);
writeln!(writer, "Index\tVendor ID\tProduct ID\tDevice Description")?;
writeln!(writer, "-----\t---------\t----------\t----------------")?;
for (
index,
ADBDeviceInfo {
vendor_id,
product_id,
device_description,
},
) in devices.iter().enumerate()
{
writeln!(
writer,
"#{}\t{:04x}\t{:04x}\t{}",
index, vendor_id, product_id, device_description
)?;
}
writer.flush()?;
return Ok(());
}
let device = match (usb_command.vendor_id, usb_command.product_id) {
(Some(vid), Some(pid)) => match usb_command.path_to_private_key {
Some(pk) => ADBUSBDevice::new_with_custom_private_key(vid, pid, pk)?,
@@ -76,7 +106,11 @@ fn main() -> Result<()> {
);
}
};
(device.boxed(), usb_command.commands)
match usb_command.commands {
Some(commands) => (device.boxed(), commands),
None => bail!("no command provided"),
}
}
MainCommand::Tcp(tcp_command) => {
let device = ADBTcpDevice::new(tcp_command.address)?;

View File

@@ -20,6 +20,9 @@ pub struct UsbCommand {
/// Path to a custom private key to use for authentication
#[clap(short = 'k', long = "private-key")]
pub path_to_private_key: Option<PathBuf>,
/// List all connected Android devices
#[clap(short = 'l', long = "list")]
pub list_devices: bool,
#[clap(subcommand)]
pub commands: DeviceCommands,
pub commands: Option<DeviceCommands>,
}

View File

@@ -35,28 +35,80 @@ pub fn read_adb_private_key<P: AsRef<Path>>(private_key_path: P) -> Result<Optio
}
}
/// Search for adb devices with known interface class and subclass values
pub fn search_adb_devices() -> Result<Option<(u16, u16)>> {
/// Represents an Android device connected via USB
#[derive(Clone, Debug)]
pub struct ADBDeviceInfo {
/// Vendor ID of the device
pub vendor_id: u16,
/// Product ID of the device
pub product_id: u16,
/// Textual description of the device
pub device_description: String,
}
/// Find and return a list of all connected Android devices with known interface class and subclass values
pub fn find_all_connected_adb_devices() -> Result<Vec<ADBDeviceInfo>> {
let mut found_devices = vec![];
for device in rusb::devices()?.iter() {
let Ok(des) = device.device_descriptor() else {
continue;
};
if is_adb_device(&device, &des) {
log::debug!(
"Autodetect device {:04x}:{:04x}",
des.vendor_id(),
des.product_id()
);
found_devices.push((des.vendor_id(), des.product_id()));
let device_handle = match device.open() {
Ok(h) => h,
Err(_) => {
found_devices.push(ADBDeviceInfo {
vendor_id: des.vendor_id(),
product_id: des.product_id(),
device_description: "Unknown device".to_string(),
});
continue;
}
};
let manufacturer = device_handle
.read_manufacturer_string_ascii(&des)
.unwrap_or_else(|_| "Unknown".to_string());
let product = device_handle
.read_product_string_ascii(&des)
.unwrap_or_else(|_| "Unknown".to_string());
let device_description = format!("{} {}", manufacturer, product);
found_devices.push(ADBDeviceInfo {
vendor_id: des.vendor_id(),
product_id: des.product_id(),
device_description,
});
}
}
Ok(found_devices)
}
/// Find and return an USB-connected Android device with known interface class and subclass values.
///
/// Returns the first device found or None if no device is found.
/// If multiple devices are found, an error is returned.
pub fn get_single_connected_adb_device() -> Result<Option<ADBDeviceInfo>> {
let found_devices = find_all_connected_adb_devices()?;
match (found_devices.first(), found_devices.get(1)) {
(None, _) => Ok(None),
(Some(identifiers), None) => Ok(Some(*identifiers)),
(Some((vid1, pid1)), Some((vid2, pid2))) => Err(RustADBError::DeviceNotFound(format!(
"Found two Android devices {vid1:04x}:{pid1:04x} and {vid2:04x}:{pid2:04x}",
(Some(device_info), None) => {
log::debug!(
"Autodetect device {:04x}:{:04x} - {}",
device_info.vendor_id,
device_info.product_id,
device_info.device_description
);
Ok(Some(device_info.clone()))
}
(Some(device_1), Some(device_2)) => Err(RustADBError::DeviceNotFound(format!(
"Found two Android devices {:04x}:{:04x} and {:04x}:{:04x}",
device_1.vendor_id, device_1.product_id, device_2.vendor_id, device_2.product_id
))),
}
}
@@ -167,10 +219,12 @@ impl ADBUSBDevice {
/// autodetect connected ADB devices and establish a connection with the first device found using a custom private key path
pub fn autodetect_with_custom_private_key(private_key_path: PathBuf) -> Result<Self> {
match search_adb_devices()? {
Some((vendor_id, product_id)) => {
ADBUSBDevice::new_with_custom_private_key(vendor_id, product_id, private_key_path)
}
match get_single_connected_adb_device()? {
Some(device_info) => ADBUSBDevice::new_with_custom_private_key(
device_info.vendor_id,
device_info.product_id,
private_key_path,
),
_ => Err(RustADBError::DeviceNotFound(
"cannot find USB devices matching the signature of an ADB device".into(),
)),

View File

@@ -12,7 +12,8 @@ use adb_message_device::ADBMessageDevice;
pub use adb_tcp_device::ADBTcpDevice;
pub use adb_transport_message::{ADBTransportMessage, ADBTransportMessageHeader};
pub use adb_usb_device::{
ADBUSBDevice, get_default_adb_key_path, is_adb_device, search_adb_devices,
ADBDeviceInfo, ADBUSBDevice, find_all_connected_adb_devices, get_default_adb_key_path,
get_single_connected_adb_device, is_adb_device,
};
pub use message_writer::MessageWriter;
pub use models::{ADBRsaKey, MessageCommand, MessageSubcommand};

View File

@@ -17,7 +17,10 @@ mod transports;
mod utils;
pub use adb_device_ext::ADBDeviceExt;
pub use device::{ADBTcpDevice, ADBUSBDevice, is_adb_device, search_adb_devices};
pub use device::{
ADBDeviceInfo, ADBTcpDevice, ADBUSBDevice, find_all_connected_adb_devices,
get_single_connected_adb_device, is_adb_device,
};
pub use emulator_device::ADBEmulatorDevice;
pub use error::{Result, RustADBError};
pub use mdns::*;