Skip to content

Commit

Permalink
feat: create an USBTransport from a rusb::Device (#66)
Browse files Browse the repository at this point in the history
This allows to be more flexible on USB device we want to connect to,
without making public API to complex
  • Loading branch information
cli-s1n authored Nov 30, 2024
1 parent 9eeb8f7 commit 2f4d13b
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 44 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/rust-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,30 @@ jobs:
- uses: actions/checkout@v4
- run: rustup component add clippy
- name: Run clippy
run : cargo clippy --all-features
run: cargo clippy --all-features

fmt:
name: "fmt"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run formatter
run : cargo fmt --all --check
run: cargo fmt --all --check

doc:
name: "doc"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run doc
run : cargo doc --all-features --keep-going
run: cargo doc --all-features --no-deps
env:
RUSTDOCFLAGS: "-D warnings"

tests:
name: "tests"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: cargo test --verbose
run: cargo test --verbose
10 changes: 5 additions & 5 deletions adb_client/src/adb_device_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ use std::path::Path;
use crate::models::AdbStatResponse;
use crate::{RebootType, Result};

/// Trait representing all features available on devices.
/// Trait representing all features available on both [`crate::ADBServerDevice`] and [`crate::ADBUSBDevice`]
pub trait ADBDeviceExt {
/// Run 'command' in a shell on the device, and write its output and error streams into `output`.
/// Runs command in a shell on the device, and write its output and error streams into output.
fn shell_command<S: ToString, W: Write>(
&mut self,
command: impl IntoIterator<Item = S>,
output: W,
) -> Result<()>;

/// Start an interactive shell session on the device.
/// Input data is read from `reader` and write to `writer`.
/// `W` has a 'static bound as it is internally used in a thread.
/// Starts an interactive shell session on the device.
/// Input data is read from reader and write to writer.
/// W has a 'static bound as it is internally used in a thread.
fn shell<R: Read, W: Write + Send + 'static>(&mut self, reader: R, writer: W) -> Result<()>;

/// Display the stat information for a remote file
Expand Down
36 changes: 28 additions & 8 deletions adb_client/src/device/adb_usb_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ use crate::ADBMessageTransport;
use crate::ADBTransport;
use crate::{Result, RustADBError, USBTransport};

/// Represent a device reached and available over USB.
#[derive(Debug)]
pub struct ADBUSBDevice {
private_key: ADBRsaKey,
inner: ADBMessageDevice<USBTransport>,
}

pub fn read_adb_private_key<P: AsRef<Path>>(private_key_path: P) -> Result<Option<ADBRsaKey>> {
Ok(read_to_string(private_key_path.as_ref()).map(|pk| {
match ADBRsaKey::new_from_pkcs8(&pk) {
Expand Down Expand Up @@ -101,6 +94,13 @@ pub fn get_default_adb_key_path() -> Result<PathBuf> {
.ok_or(RustADBError::NoHomeDirectory)
}

/// Represent a device reached and available over USB.
#[derive(Debug)]
pub struct ADBUSBDevice {
private_key: ADBRsaKey,
inner: ADBMessageDevice<USBTransport>,
}

impl ADBUSBDevice {
/// Instantiate a new [`ADBUSBDevice`]
pub fn new(vendor_id: u16, product_id: u16) -> Result<Self> {
Expand All @@ -112,6 +112,26 @@ impl ADBUSBDevice {
vendor_id: u16,
product_id: u16,
private_key_path: PathBuf,
) -> Result<Self> {
Self::new_from_transport_inner(USBTransport::new(vendor_id, product_id)?, private_key_path)
}

/// Instantiate a new [`ADBUSBDevice`] from a [`USBTransport`] and an optional private key path.
pub fn new_from_transport(
transport: USBTransport,
private_key_path: Option<PathBuf>,
) -> Result<Self> {
let private_key_path = match private_key_path {
Some(private_key_path) => private_key_path,
None => get_default_adb_key_path()?,
};

Self::new_from_transport_inner(transport, private_key_path)
}

fn new_from_transport_inner(
transport: USBTransport,
private_key_path: PathBuf,
) -> Result<Self> {
let private_key = match read_adb_private_key(private_key_path)? {
Some(pk) => pk,
Expand All @@ -120,7 +140,7 @@ impl ADBUSBDevice {

let mut s = Self {
private_key,
inner: ADBMessageDevice::new(USBTransport::new(vendor_id, product_id)),
inner: ADBMessageDevice::new(transport),
};

s.connect()?;
Expand Down
2 changes: 1 addition & 1 deletion adb_client/src/server_device/commands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
};

impl ADBServerDevice {
/// Lists files in `path` on the device.
/// Lists files in path on the device.
pub fn list<A: AsRef<str>>(&mut self, path: A) -> Result<()> {
let serial = self.identifier.clone();
self.connect()?
Expand Down
2 changes: 1 addition & 1 deletion adb_client/src/server_device/commands/recv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl<R: Read> Read for ADBRecvCommandReader<R> {
}

impl ADBServerDevice {
/// Receives `path` to `stream` from the device.
/// Receives path to stream from the device.
pub fn pull<A: AsRef<str>>(&mut self, path: A, stream: &mut dyn Write) -> Result<()> {
let serial = self.identifier.clone();
self.connect()?
Expand Down
2 changes: 1 addition & 1 deletion adb_client/src/server_device/commands/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<W: Write> Write for ADBSendCommandWriter<W> {
}

impl ADBServerDevice {
/// Send `stream` to `path` on the device.
/// Send stream to path on the device.
pub fn push<R: Read, A: AsRef<str>>(&mut self, stream: R, path: A) -> Result<()> {
log::info!("Sending data to {}", path.as_ref());
let serial = self.identifier.clone();
Expand Down
2 changes: 1 addition & 1 deletion adb_client/src/server_device/commands/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl ADBServerDevice {
}
}

/// Stat file given as `path` on the device.
/// Stat file given as path on the device.
pub fn stat<A: AsRef<str>>(&mut self, path: A) -> Result<AdbStatResponse> {
let serial = self.identifier.clone();
self.connect()?
Expand Down
50 changes: 27 additions & 23 deletions adb_client/src/transports/usb_transport.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{sync::Arc, time::Duration};

use rusb::{
constants::LIBUSB_CLASS_VENDOR_SPEC, DeviceHandle, Direction, GlobalContext, TransferType,
constants::LIBUSB_CLASS_VENDOR_SPEC, Device, DeviceHandle, Direction, GlobalContext,
TransferType,
};

use super::{ADBMessageTransport, ADBTransport};
Expand All @@ -19,18 +20,35 @@ struct Endpoint {
/// Transport running on USB
#[derive(Debug, Clone)]
pub struct USBTransport {
vendor_id: u16,
product_id: u16,
device: Device<GlobalContext>,
handle: Option<Arc<DeviceHandle<GlobalContext>>>,
}

impl USBTransport {
/// Instantiate a new [USBTransport]
pub fn new(vendor_id: u16, product_id: u16) -> Self {
/// Instantiate a new [`USBTransport`].
/// Only the first device with given vendor_id and product_id is returned.
pub fn new(vendor_id: u16, product_id: u16) -> Result<Self> {
for device in rusb::devices()?.iter() {
if let Ok(descriptor) = device.device_descriptor() {
if descriptor.vendor_id() == vendor_id && descriptor.product_id() == product_id {
return Ok(Self::new_from_device(device));
}
}
}

Err(RustADBError::DeviceNotFound(format!(
"cannot find USB device with vendor_id={} and product_id={}",
vendor_id, product_id
)))
}

/// Instantiate a new [`USBTransport`] from a [`rusb::Device`].
///
/// Devices can be enumerated using [`rusb::devices()`] and then filtered out to get desired device.
pub fn new_from_device(rusb_device: rusb::Device<GlobalContext>) -> Self {
Self {
device: rusb_device,
handle: None,
vendor_id,
product_id,
}
}

Expand Down Expand Up @@ -112,22 +130,8 @@ impl USBTransport {

impl ADBTransport for USBTransport {
fn connect(&mut self) -> crate::Result<()> {
for d in rusb::devices()?.iter() {
if let Ok(descriptor) = d.device_descriptor() {
if descriptor.vendor_id() == self.vendor_id
&& descriptor.product_id() == self.product_id
{
self.handle = Some(Arc::new(d.open()?));

return Ok(());
}
}
}

Err(RustADBError::DeviceNotFound(format!(
"Cannot find device with vendor id {} and product id {}",
self.vendor_id, self.product_id
)))
self.handle = Some(Arc::new(self.device.open()?));
Ok(())
}

fn disconnect(&mut self) -> crate::Result<()> {
Expand Down

0 comments on commit 2f4d13b

Please sign in to comment.