diff --git a/.github/workflows/rust-build.yml b/.github/workflows/rust-build.yml index 5be9de5..062b435 100644 --- a/.github/workflows/rust-build.yml +++ b/.github/workflows/rust-build.yml @@ -1,6 +1,6 @@ name: Rust - Build -on: [push] +on: [push, pull_request] env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/rust-quality.yml b/.github/workflows/rust-quality.yml index fa1ff09..bf8d89b 100644 --- a/.github/workflows/rust-quality.yml +++ b/.github/workflows/rust-quality.yml @@ -1,6 +1,6 @@ name: Rust - Quality -on: [push] +on: [push, pull_request] env: CARGO_TERM_COLOR: always diff --git a/adb_cli/src/commands/usb.rs b/adb_cli/src/commands/usb.rs index 9aff77d..99d70d6 100644 --- a/adb_cli/src/commands/usb.rs +++ b/adb_cli/src/commands/usb.rs @@ -13,10 +13,10 @@ fn parse_hex_id(id: &str) -> Result { pub struct UsbCommand { /// Hexadecimal vendor id of this USB device #[clap(short = 'v', long = "vendor-id", value_parser=parse_hex_id, value_name="VID")] - pub vendor_id: u16, + pub vendor_id: Option, /// Hexadecimal product id of this USB device #[clap(short = 'p', long = "product-id", value_parser=parse_hex_id, value_name="PID")] - pub product_id: u16, + pub product_id: Option, /// Path to a custom private key to use for authentication #[clap(short = 'k', long = "private-key")] pub path_to_private_key: Option, diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs index 0f21b92..3a4dac1 100644 --- a/adb_cli/src/main.rs +++ b/adb_cli/src/main.rs @@ -158,11 +158,20 @@ fn main() -> Result<()> { } } Command::Usb(usb) => { - let mut device = match usb.path_to_private_key { - Some(pk) => { - ADBUSBDevice::new_with_custom_private_key(usb.vendor_id, usb.product_id, pk)? + let mut device = match (usb.vendor_id, usb.product_id) { + (Some(vid), Some(pid)) => match usb.path_to_private_key { + Some(pk) => ADBUSBDevice::new_with_custom_private_key(vid, pid, pk)?, + None => ADBUSBDevice::new(vid, pid)?, + }, + + (None, None) => match usb.path_to_private_key { + Some(pk) => ADBUSBDevice::autodetect_with_custom_private_key(pk)?, + None => ADBUSBDevice::autodetect()?, + }, + + _ => { + anyhow::bail!("please either supply values for both the --vendor-id and --product-id flags or none."); } - None => ADBUSBDevice::new(usb.vendor_id, usb.product_id)?, }; match usb.commands { diff --git a/adb_client/src/usb/adb_usb_device.rs b/adb_client/src/usb/adb_usb_device.rs index 49b5aeb..e4fda72 100644 --- a/adb_client/src/usb/adb_usb_device.rs +++ b/adb_client/src/usb/adb_usb_device.rs @@ -1,5 +1,8 @@ use byteorder::ReadBytesExt; use rand::Rng; +use rusb::Device; +use rusb::DeviceDescriptor; +use rusb::UsbContext; use std::fs::read_to_string; use std::io::Cursor; use std::io::Read; @@ -37,29 +40,77 @@ fn read_adb_private_key>(private_key_path: P) -> Result Result> { + 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())); + } + } -impl ADBUSBDevice { - /// Instantiate a new [ADBUSBDevice] - pub fn new(vendor_id: u16, product_id: u16) -> Result { - let private_key_path = homedir::my_home() - .ok() - .flatten() - .map(|home| home.join(".android").join("adbkey")) - .ok_or(RustADBError::NoHomeDirectory)?; + match (found_devices.get(0), 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 {:04x}:{:04x} and {:04x}:{:04x}", + vid1, pid1, vid2, pid2 + ))), + } +} - let private_key = match read_adb_private_key(private_key_path)? { - Some(pk) => pk, - None => ADBRsaKey::random_with_size(2048)?, - }; +fn is_adb_device(device: &Device, des: &DeviceDescriptor) -> bool { + const ADB_CLASS: u8 = 0xff; - let mut s = Self { - private_key, - transport: USBTransport::new(vendor_id, product_id), + const ADB_SUBCLASS: u8 = 0x42; + const ADB_PROTOCOL: u8 = 0x1; + + // Some devices require choosing the file transfer mode + // for usb debugging to take effect. + const BULK_CLASS: u8 = 0xdc; + const BULK_ADB_SUBCLASS: u8 = 2; + + for n in 0..des.num_configurations() { + let Ok(config_des) = device.config_descriptor(n) else { + continue; }; + for interface in config_des.interfaces() { + for interface_des in interface.descriptors() { + let proto = interface_des.protocol_code(); + let class = interface_des.class_code(); + let subcl = interface_des.sub_class_code(); + if proto == ADB_PROTOCOL + && ((class == ADB_CLASS && subcl == ADB_SUBCLASS) + || (class == BULK_CLASS && subcl == BULK_ADB_SUBCLASS)) + { + return true; + } + } + } + } + false +} - s.connect()?; +fn get_default_adb_key_path() -> Result { + homedir::my_home() + .ok() + .flatten() + .map(|home| home.join(".android").join("adbkey")) + .ok_or(RustADBError::NoHomeDirectory) +} - Ok(s) +impl ADBUSBDevice { + /// Instantiate a new [ADBUSBDevice] + pub fn new(vendor_id: u16, product_id: u16) -> Result { + Self::new_with_custom_private_key(vendor_id, product_id, get_default_adb_key_path()?) } /// Instantiate a new [ADBUSBDevice] using a custom private key path @@ -83,6 +134,23 @@ impl ADBUSBDevice { Ok(s) } + /// autodetect connected ADB devices and establish a connection with the first device found + pub fn autodetect() -> Result { + Self::autodetect_with_custom_private_key(get_default_adb_key_path()?) + } + + /// 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 { + match search_adb_devices()? { + Some((vendor_id, product_id)) => { + ADBUSBDevice::new_with_custom_private_key(vendor_id, product_id, private_key_path) + } + _ => Err(RustADBError::DeviceNotFound( + "cannot find USB devices matching the signature of an ADB device".into(), + )), + } + } + /// Send initial connect pub fn connect(&mut self) -> Result<()> { self.transport.connect()?;