Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: autodetect an ADB device when no vendor or product ID is specified #44

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust-build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Rust - Build

on: [push]
on: [push, pull_request]

env:
CARGO_TERM_COLOR: always
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rust-quality.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Rust - Quality

on: [push]
on: [push, pull_request]

env:
CARGO_TERM_COLOR: always
Expand Down
4 changes: 2 additions & 2 deletions adb_cli/src/commands/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ fn parse_hex_id(id: &str) -> Result<u16, ParseIntError> {
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<u16>,
/// 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<u16>,
/// Path to a custom private key to use for authentication
#[clap(short = 'k', long = "private-key")]
pub path_to_private_key: Option<PathBuf>,
Expand Down
17 changes: 13 additions & 4 deletions adb_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
102 changes: 85 additions & 17 deletions adb_client/src/usb/adb_usb_device.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -37,29 +40,77 @@ fn read_adb_private_key<P: AsRef<Path>>(private_key_path: P) -> Result<Option<AD
}
})
}
/// Search for adb devices with known interface class and subclass values
fn search_adb_devices() -> Result<Option<(u16, u16)>> {
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<Self> {
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<T: UsbContext>(device: &Device<T>, 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<PathBuf> {
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> {
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
Expand All @@ -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> {
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<Self> {
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()?;
Expand Down