diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 673556c2..f13e6d44 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -35,4 +35,4 @@ jobs: with: files: | target/wheels/pyadb_client*.whl - target/wheels/pyadb_client*.tar.gz \ No newline at end of file + target/wheels/pyadb_client*.tar.gz diff --git a/.github/workflows/rust-build.yml b/.github/workflows/rust-build.yml index ca0a9b2e..f2aa3caa 100644 --- a/.github/workflows/rust-build.yml +++ b/.github/workflows/rust-build.yml @@ -26,4 +26,4 @@ jobs: python-version: "3.10" - name: Build project - run: cargo build --release --all-features + run: cargo build --release --features rusb,mdns diff --git a/.github/workflows/rust-quality.yml b/.github/workflows/rust-quality.yml index bfc54502..0e4124cb 100644 --- a/.github/workflows/rust-quality.yml +++ b/.github/workflows/rust-quality.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - run: rustup component add clippy - name: Run clippy - run: cargo clippy --all-features + run: cargo clippy --features rusb,mdns fmt: name: "fmt" @@ -33,7 +33,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run doc - run: cargo doc --all-features --no-deps + run: cargo doc --features rusb,mdns --no-deps env: RUSTDOCFLAGS: "-D warnings" @@ -43,4 +43,4 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run tests - run: cargo test --verbose --all-features + run: cargo test --verbose --features rusb,mdns diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 2ee70590..1b0b08cc 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -36,7 +36,7 @@ jobs: CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} - name: Build release - run: cargo build --all-features --release + run: cargo build -p adb_cli --release - name: Rename binary run: mv target/release/adb_cli target/release/adb_cli-linux @@ -69,7 +69,7 @@ jobs: override: true - name: Build release - run: cargo build --all-features --release + run: cargo build -p adb_cli --release - name: Rename binary run: mv target/release/adb_cli target/release/adb_cli-macos @@ -98,7 +98,7 @@ jobs: python-version: "3.10" - name: Build release - run: cargo build --all-features --release + run: cargo build -p adb_cli --release - name: Rename binary run: Rename-Item -Path target/release/adb_cli.exe -NewName adb_cli-windows.exe diff --git a/.gitignore b/.gitignore index a7d61ceb..a3ae0cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ target /.vscode venv /.mypy_cache -pyadb_client/pyadb_client.pyi \ No newline at end of file +pyadb_client/pyadb_client.pyi diff --git a/Cargo.toml b/Cargo.toml index 0253cd07..36adac83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["adb_cli", "adb_client", "pyadb_client"] +members = ["adb_cli", "adb_client", "examples/mdns", "pyadb_client"] resolver = "2" [workspace.package] diff --git a/README.md b/README.md index 6fd1369e..02516bb0 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,12 @@ Provides a "real-world" usage example of this library. Improved documentation available [here](./adb_cli/README.md). +## examples + +Some examples are available in the `examples` directory: + +- `examples/mdns`: mDNS device discovery example + ## pyadb_client Python wrapper using `adb_client` library to export classes usable directly from a Python environment. diff --git a/adb_cli/Cargo.toml b/adb_cli/Cargo.toml index 0d31a5d3..760cf5e7 100644 --- a/adb_cli/Cargo.toml +++ b/adb_cli/Cargo.toml @@ -11,7 +11,7 @@ rust-version.workspace = true version.workspace = true [dependencies] -adb_client = { version = "^2.1.17" } +adb_client = { version = "^2.1.17", features = ["mdns", "rusb"] } anyhow = { version = "1.0.100" } clap = { version = "4.5.51", features = ["derive"] } env_logger = { version = "0.11.8" } diff --git a/adb_cli/src/handlers/emulator_commands.rs b/adb_cli/src/handlers/emulator_commands.rs index 5855cd7b..dcd6c162 100644 --- a/adb_cli/src/handlers/emulator_commands.rs +++ b/adb_cli/src/handlers/emulator_commands.rs @@ -1,4 +1,4 @@ -use adb_client::ADBEmulatorDevice; +use adb_client::emulator::ADBEmulatorDevice; use crate::models::{EmuCommand, EmulatorCommand}; diff --git a/adb_cli/src/handlers/host_commands.rs b/adb_cli/src/handlers/host_commands.rs index 5a7c7ee1..ba6e61c3 100644 --- a/adb_cli/src/handlers/host_commands.rs +++ b/adb_cli/src/handlers/host_commands.rs @@ -1,4 +1,7 @@ -use adb_client::{ADBServer, DeviceShort, MDNSBackend, Result, WaitForDeviceState}; +use adb_client::{ + Result, + server::{ADBServer, DeviceShort, MDNSBackend, WaitForDeviceState}, +}; use crate::models::{HostCommand, MdnsCommand, ServerCommand}; diff --git a/adb_cli/src/handlers/local_commands.rs b/adb_cli/src/handlers/local_commands.rs index 101310d2..ea67a54c 100644 --- a/adb_cli/src/handlers/local_commands.rs +++ b/adb_cli/src/handlers/local_commands.rs @@ -1,6 +1,6 @@ use std::{fs::File, io::Write}; -use adb_client::ADBServerDevice; +use adb_client::server_device::ADBServerDevice; use anyhow::{Result, anyhow}; use crate::models::LocalDeviceCommand; diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs index 32e63044..036a738d 100644 --- a/adb_cli/src/main.rs +++ b/adb_cli/src/main.rs @@ -7,9 +7,12 @@ mod handlers; mod models; mod utils; -use adb_client::{ - ADBDeviceExt, ADBServer, ADBServerDevice, ADBTcpDevice, ADBUSBDevice, MDNSDiscoveryService, -}; +use adb_client::ADBDeviceExt; +use adb_client::mdns::MDNSDiscoveryService; +use adb_client::server::ADBServer; +use adb_client::server_device::ADBServerDevice; +use adb_client::tcp::ADBTcpDevice; +use adb_client::usb::ADBRusbDevice; #[cfg(any(target_os = "linux", target_os = "macos"))] use adb_termios::ADBTermios; @@ -63,12 +66,12 @@ fn main() -> Result<()> { MainCommand::Usb(usb_command) => { 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)?, - None => ADBUSBDevice::new(vid, pid)?, + Some(pk) => ADBRusbDevice::new_with_custom_private_key(vid, pid, pk)?, + None => ADBRusbDevice::new(vid, pid)?, }, (None, None) => match usb_command.path_to_private_key { - Some(pk) => ADBUSBDevice::autodetect_with_custom_private_key(pk)?, - None => ADBUSBDevice::autodetect()?, + Some(pk) => ADBRusbDevice::autodetect_with_custom_private_key(pk)?, + None => ADBRusbDevice::autodetect()?, }, _ => { anyhow::bail!( diff --git a/adb_cli/src/models/host.rs b/adb_cli/src/models/host.rs index 4161d18e..6904d39d 100644 --- a/adb_cli/src/models/host.rs +++ b/adb_cli/src/models/host.rs @@ -1,6 +1,6 @@ use std::net::SocketAddrV4; -use adb_client::{RustADBError, WaitForDeviceTransport}; +use adb_client::{RustADBError, server::WaitForDeviceTransport}; use clap::Parser; fn parse_wait_for_device_device_transport( diff --git a/adb_client/Cargo.toml b/adb_client/Cargo.toml index 6944a40e..985380c1 100644 --- a/adb_client/Cargo.toml +++ b/adb_client/Cargo.toml @@ -10,6 +10,16 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = [] +mdns = ["dep:mdns-sd"] +rusb = ["dep:rsa", "dep:rusb"] +webusb = ["dep:webusb-web", "dep:rsa"] + [dependencies] base64 = { version = "0.22.1" } bincode = { version = "2.0.1", features = ["serde"] } @@ -17,24 +27,40 @@ byteorder = { version = "1.5.0" } chrono = { version = "0.4.42", default-features = false, features = ["std"] } image = { version = "0.25.8", default-features = false } log = { version = "0.4.28" } -mdns-sd = { version = "0.17.0", default-features = false, features = [ - "logging", -] } -num-bigint = { version = "0.8.5", package = "num-bigint-dig" } +num-bigint = { version = "0.8.4", package = "num-bigint-dig" } num-traits = { version = "0.2.19" } quick-protobuf = { version = "0.8.1" } -rand = { version = "0.9.2" } -rcgen = { version = "0.14.5" } +rand = { version = "0.8.5" } +rcgen = { version = "0.13.2", default-features = false, features = [ + "pem", + "ring" +] } regex = { version = "1.12.2", features = ["perf", "std", "unicode"] } -rsa = { version = "0.9.8" } -rusb = { version = "0.9.4", features = ["vendored"] } -rustls = { version = "0.23.35" } -rustls-pki-types = { version = "1.13.0" } +rustls = { version = "0.23.33", default-features = false, features = ["logging", "ring", "std", "tls12"] } +rustls-pki-types = { version = "1.12.0", features = ["web"] } serde = { version = "1.0.228", features = ["derive"] } serde_repr = { version = "0.1.20" } sha1 = { version = "0.10.6", features = ["oid"] } thiserror = { version = "2.0.17" } +######### +# MDNS dependencies +mdns-sd = { version = "0.17.0", default-features = false, features = [ + "logging", +], optional = true } +######### +######### +# USB-only dependencies +rsa = { version = "0.9.7", optional = true } +rusb = { version = "0.9.4", features = ["vendored"], optional = true } +######### +######### +# webusb dependencies +webusb-web = { version = "0.4.1", optional = true } +getrandom = { version = "0.2.16", features = ["js"], optional = true } +ring = { version = "0.17.14", features = ["wasm32_unknown_unknown_js"], optional = true } + + [dev-dependencies] anyhow = { version = "1.0.100" } criterion = { version = "0.7.0" } # Used for benchmarks diff --git a/adb_client/README.md b/adb_client/README.md index bc8af35b..2664051c 100644 --- a/adb_client/README.md +++ b/adb_client/README.md @@ -16,97 +16,36 @@ Add `adb_client` crate as a dependency by simply adding it to your `Cargo.toml`: adb_client = "*" ``` -## Benchmarks - -Benchmarks run on `v2.0.6`, on a **Samsung S10 SM-G973F** device and an **Intel i7-1265U** CPU laptop - -### `ADBServerDevice` push vs `adb push` - -`ADBServerDevice` performs all operations by using adb server as a bridge. - -|File size|Sample size|`ADBServerDevice`|`adb`|Difference| -|:-------:|:---------:|:----------:|:---:|:-----:| -|10 MB|100|350,79 ms|356,30 ms|
-1,57 %
| -|500 MB|50|15,60 s|15,64 s|
-0,25 %
| -|1 GB|20|31,09 s|31,12 s|
-0,10 %
| - -## Examples - -### Get available ADB devices - -```rust no_run -use adb_client::ADBServer; -use std::net::{SocketAddrV4, Ipv4Addr}; - -// A custom server address can be provided -let server_ip = Ipv4Addr::new(127, 0, 0, 1); -let server_port = 5037; - -let mut server = ADBServer::new(SocketAddrV4::new(server_ip, server_port)); -server.devices(); -``` - -### Using ADB server as bridge +## Crate features -#### Launch a command on device +| Feature | Description | Default? | +| :-----: | :---------------------------------------------: | :------: | +| `mdns` | Enables mDNS device discovery on local network. | No | +| `rusb` | Enables interactions with USB devices. | No | -```rust no_run -use adb_client::{ADBServer, ADBDeviceExt}; +To deactivate some features you can use the `default-features = false` option in your `Cargo.toml` file and manually specify the features you want to activate: -let mut server = ADBServer::default(); -let mut device = server.get_device().expect("cannot get device"); -device.shell_command(&["df", "-h"], &mut std::io::stdout()); -``` - -#### Push a file to the device - -```rust no_run -use adb_client::ADBServer; -use std::net::Ipv4Addr; -use std::fs::File; -use std::path::Path; - -let mut server = ADBServer::default(); -let mut device = server.get_device().expect("cannot get device"); -let mut input = File::open(Path::new("/tmp/f")).expect("Cannot open file"); -device.push(&mut input, "/data/local/tmp"); +```toml +[dependencies] +adb_client = { version = "*" } ``` -### Interact directly with end devices - -#### (USB) Launch a command on device - -```rust no_run -use adb_client::{ADBUSBDevice, ADBDeviceExt}; +## Examples -let vendor_id = 0x04e8; -let product_id = 0x6860; -let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device"); -device.shell_command(&["df", "-h"], &mut std::io::stdout()); -``` +Usage examples can be found in the `examples/` directory of this repository. -#### (USB) Push a file to the device +Some example are also provided in the various `README.md` files of modules. -```rust no_run -use adb_client::{ADBUSBDevice, ADBDeviceExt}; -use std::fs::File; -use std::path::Path; +## Benchmarks -let vendor_id = 0x04e8; -let product_id = 0x6860; -let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device"); -let mut input = File::open(Path::new("/tmp/f")).expect("Cannot open file"); -device.push(&mut input, &"/data/local/tmp"); -``` +Benchmarks run on `v2.0.6`, on a **Samsung S10 SM-G973F** device and an **Intel i7-1265U** CPU laptop -#### (TCP) Get a shell from device +### `ADBServerDevice` push vs `adb push` -```rust no_run -use std::net::{SocketAddr, IpAddr, Ipv4Addr}; -use adb_client::{ADBTcpDevice, ADBDeviceExt}; +`ADBServerDevice` performs all operations by using adb server as a bridge. -let device_ip = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 10)); -let device_port = 43210; -let mut device = ADBTcpDevice::new(SocketAddr::new(device_ip, device_port)).expect("cannot find device"); -device.shell(&mut std::io::stdin(), Box::new(std::io::stdout())); -``` +| File size | Sample size | `ADBServerDevice` | `adb` | Difference | +| :-------: | :---------: | :---------------: | :-------: | :------------------------------------: | +| 10 MB | 100 | 350,79 ms | 356,30 ms |
-1,57 %
| +| 500 MB | 50 | 15,60 s | 15,64 s |
-0,25 %
| +| 1 GB | 20 | 31,09 s | 31,12 s |
-0,10 %
| diff --git a/adb_client/src/adb_device_ext.rs b/adb_client/src/adb_device_ext.rs index a342f3a5..1eeb8f47 100644 --- a/adb_client/src/adb_device_ext.rs +++ b/adb_client/src/adb_device_ext.rs @@ -6,7 +6,10 @@ use image::{ImageBuffer, ImageFormat, Rgba}; use crate::models::AdbStatResponse; use crate::{RebootType, Result}; -/// Trait representing all features available on both [`crate::ADBServerDevice`] and [`crate::ADBUSBDevice`] +/// Trait representing all features available on an ADB device, currently used by: +/// - [`crate::server_device::ADBServerDevice`] +/// - [`crate::usb::ADBRusbDevice`] +/// - [`crate::tcp::ADBTcpDevice`] pub trait ADBDeviceExt { /// Runs command in a shell on the device, and write its output and error streams into output. fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()>; diff --git a/adb_client/src/transports/traits/adb_transport.rs b/adb_client/src/adb_transport.rs similarity index 100% rename from adb_client/src/transports/traits/adb_transport.rs rename to adb_client/src/adb_transport.rs diff --git a/adb_client/src/constants.rs b/adb_client/src/constants.rs deleted file mode 100644 index c2eb05ee..00000000 --- a/adb_client/src/constants.rs +++ /dev/null @@ -1 +0,0 @@ -pub const BUFFER_SIZE: usize = 65536; diff --git a/adb_client/src/device/adb_usb_device.rs b/adb_client/src/device/adb_usb_device.rs deleted file mode 100644 index 9de56aff..00000000 --- a/adb_client/src/device/adb_usb_device.rs +++ /dev/null @@ -1,257 +0,0 @@ -use rusb::Device; -use rusb::DeviceDescriptor; -use rusb::UsbContext; -use rusb::constants::LIBUSB_CLASS_VENDOR_SPEC; -use std::fs::read_to_string; -use std::io::Read; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; - -use super::adb_message_device::ADBMessageDevice; -use super::models::MessageCommand; -use super::{ADBRsaKey, ADBTransportMessage}; -use crate::ADBDeviceExt; -use crate::ADBMessageTransport; -use crate::ADBTransport; -use crate::{Result, RustADBError, USBTransport}; - -pub fn read_adb_private_key>(private_key_path: P) -> Result> { - // Try to read the private key file from given path - // If the file is not found, return None - // If there is another error while reading the file, return this error - // Else, return the private key content - let pk = match read_to_string(private_key_path.as_ref()) { - Ok(pk) => pk, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), - Err(e) => return Err(e.into()), - }; - - match ADBRsaKey::new_from_pkcs8(&pk) { - Ok(pk) => Ok(Some(pk)), - Err(e) => Err(e), - } -} - -/// Search for adb devices with known interface class and subclass values -pub fn search_adb_devices() -> 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())); - } - } - - 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}", - ))), - } -} - -/// Check whether a device with given descriptor is an ADB device -pub fn is_adb_device(device: &Device, des: &DeviceDescriptor) -> bool { - 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 == LIBUSB_CLASS_VENDOR_SPEC && subcl == ADB_SUBCLASS) - || (class == BULK_CLASS && subcl == BULK_ADB_SUBCLASS)) - { - return true; - } - } - } - } - false -} - -pub fn get_default_adb_key_path() -> Result { - std::env::home_dir() - .map(|home| home.join(".android").join("adbkey")) - .ok_or(RustADBError::NoHomeDirectory) -} - -/// Represent a device reached and available over USB. -#[derive(Debug)] -pub struct ADBUSBDevice { - private_key: ADBRsaKey, - inner: ADBMessageDevice, -} - -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 - pub fn new_with_custom_private_key( - vendor_id: u16, - product_id: u16, - private_key_path: PathBuf, - ) -> Result { - 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, - ) -> Result { - 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 { - let private_key = if let Some(private_key) = read_adb_private_key(private_key_path)? { - private_key - } else { - log::warn!( - "No private key found at path {}. Using a temporary random one.", - private_key_path.display() - ); - ADBRsaKey::new_random()? - }; - - let mut s = Self { - private_key, - inner: ADBMessageDevice::new(transport), - }; - - s.connect()?; - - 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.get_transport_mut().connect()?; - - let message = ADBTransportMessage::new( - MessageCommand::Cnxn, - 0x0100_0000, - 1_048_576, - format!("host::{}\0", env!("CARGO_PKG_NAME")).as_bytes(), - ); - - self.get_transport_mut().write_message(message)?; - - let message = self.get_transport_mut().read_message()?; - // If the device returned CNXN instead of AUTH it does not require authentication, - // so we can skip the auth steps. - if message.header().command() == MessageCommand::Cnxn { - return Ok(()); - } - - message.assert_command(MessageCommand::Auth)?; - self.inner.auth_handshake(message, &self.private_key) - } - - #[inline] - fn get_transport_mut(&mut self) -> &mut USBTransport { - self.inner.get_transport_mut() - } -} - -impl ADBDeviceExt for ADBUSBDevice { - #[inline] - fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { - self.inner.shell_command(command, output) - } - - #[inline] - fn shell<'a>(&mut self, reader: &mut dyn Read, writer: Box) -> Result<()> { - self.inner.shell(reader, writer) - } - - #[inline] - fn stat(&mut self, remote_path: &str) -> Result { - self.inner.stat(remote_path) - } - - #[inline] - fn pull(&mut self, source: &dyn AsRef, output: &mut dyn Write) -> Result<()> { - self.inner.pull(source, output) - } - - #[inline] - fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { - self.inner.push(stream, path) - } - - #[inline] - fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { - self.inner.reboot(reboot_type) - } - - #[inline] - fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { - self.inner.install(apk_path) - } - - #[inline] - fn uninstall(&mut self, package: &str) -> Result<()> { - self.inner.uninstall(package) - } - - #[inline] - fn framebuffer_inner(&mut self) -> Result, Vec>> { - self.inner.framebuffer_inner() - } -} - -impl Drop for ADBUSBDevice { - fn drop(&mut self) { - // Best effort here - let _ = self.get_transport_mut().disconnect(); - } -} diff --git a/adb_client/src/device/mod.rs b/adb_client/src/device/mod.rs deleted file mode 100644 index a34b6a18..00000000 --- a/adb_client/src/device/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod adb_message_device; -mod adb_message_device_commands; -mod adb_tcp_device; -mod adb_transport_message; -mod adb_usb_device; -mod commands; -mod message_writer; -mod models; -mod shell_message_writer; - -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, -}; -pub use message_writer::MessageWriter; -pub use models::{ADBRsaKey, MessageCommand, MessageSubcommand}; -pub use shell_message_writer::ShellMessageWriter; diff --git a/adb_client/src/device/models/mod.rs b/adb_client/src/device/models/mod.rs deleted file mode 100644 index 7f97ceb9..00000000 --- a/adb_client/src/device/models/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod adb_rsa_key; -mod message_commands; - -pub use adb_rsa_key::ADBRsaKey; -pub use message_commands::{MessageCommand, MessageSubcommand}; diff --git a/adb_client/src/emulator_device/adb_emulator_device.rs b/adb_client/src/emulator/adb_emulator_device.rs similarity index 94% rename from adb_client/src/emulator_device/adb_emulator_device.rs rename to adb_client/src/emulator/adb_emulator_device.rs index 60cfaa62..d92b5497 100644 --- a/adb_client/src/emulator_device/adb_emulator_device.rs +++ b/adb_client/src/emulator/adb_emulator_device.rs @@ -3,7 +3,10 @@ use std::{ sync::LazyLock, }; -use crate::{ADBServerDevice, ADBTransport, Result, RustADBError, TCPEmulatorTransport}; +use crate::{ + ADBTransport, Result, RustADBError, emulator::tcp_emulator_transport::TCPEmulatorTransport, + server_device::ADBServerDevice, +}; use regex::Regex; static EMULATOR_REGEX: LazyLock = LazyLock::new(|| { diff --git a/adb_client/src/emulator_device/commands/mod.rs b/adb_client/src/emulator/commands/mod.rs similarity index 100% rename from adb_client/src/emulator_device/commands/mod.rs rename to adb_client/src/emulator/commands/mod.rs diff --git a/adb_client/src/emulator_device/commands/rotate.rs b/adb_client/src/emulator/commands/rotate.rs similarity index 72% rename from adb_client/src/emulator_device/commands/rotate.rs rename to adb_client/src/emulator/commands/rotate.rs index 0da9d3ea..d4e9db4f 100644 --- a/adb_client/src/emulator_device/commands/rotate.rs +++ b/adb_client/src/emulator/commands/rotate.rs @@ -1,4 +1,7 @@ -use crate::{ADBEmulatorDevice, Result, emulator_device::ADBEmulatorCommand}; +use crate::{ + Result, + emulator::{ADBEmulatorCommand, ADBEmulatorDevice}, +}; impl ADBEmulatorDevice { /// Send a SMS to this emulator with given content with given phone number diff --git a/adb_client/src/emulator_device/commands/sms.rs b/adb_client/src/emulator/commands/sms.rs similarity index 80% rename from adb_client/src/emulator_device/commands/sms.rs rename to adb_client/src/emulator/commands/sms.rs index 627feb9e..e988db38 100644 --- a/adb_client/src/emulator_device/commands/sms.rs +++ b/adb_client/src/emulator/commands/sms.rs @@ -1,4 +1,7 @@ -use crate::{ADBEmulatorDevice, Result, emulator_device::ADBEmulatorCommand}; +use crate::{ + Result, + emulator::{ADBEmulatorCommand, ADBEmulatorDevice}, +}; impl ADBEmulatorDevice { /// Send a SMS to this emulator with given content with given phone number diff --git a/adb_client/src/emulator_device/mod.rs b/adb_client/src/emulator/mod.rs similarity index 61% rename from adb_client/src/emulator_device/mod.rs rename to adb_client/src/emulator/mod.rs index d3f04502..3f799e88 100644 --- a/adb_client/src/emulator_device/mod.rs +++ b/adb_client/src/emulator/mod.rs @@ -1,5 +1,7 @@ mod adb_emulator_device; mod commands; mod models; +mod tcp_emulator_transport; + pub use adb_emulator_device::ADBEmulatorDevice; -pub(crate) use models::ADBEmulatorCommand; +use models::ADBEmulatorCommand; diff --git a/adb_client/src/emulator_device/models/adb_emulator_command.rs b/adb_client/src/emulator/models/adb_emulator_command.rs similarity index 100% rename from adb_client/src/emulator_device/models/adb_emulator_command.rs rename to adb_client/src/emulator/models/adb_emulator_command.rs diff --git a/adb_client/src/emulator_device/models/mod.rs b/adb_client/src/emulator/models/mod.rs similarity index 100% rename from adb_client/src/emulator_device/models/mod.rs rename to adb_client/src/emulator/models/mod.rs diff --git a/adb_client/src/transports/tcp_emulator_transport.rs b/adb_client/src/emulator/tcp_emulator_transport.rs similarity index 97% rename from adb_client/src/transports/tcp_emulator_transport.rs rename to adb_client/src/emulator/tcp_emulator_transport.rs index b93c4ccf..2ee05abe 100644 --- a/adb_client/src/transports/tcp_emulator_transport.rs +++ b/adb_client/src/emulator/tcp_emulator_transport.rs @@ -4,8 +4,9 @@ use std::{ net::{SocketAddrV4, TcpStream}, }; -use super::ADBTransport; -use crate::{Result, RustADBError, emulator_device::ADBEmulatorCommand}; +use crate::{ + Result, RustADBError, adb_transport::ADBTransport, emulator::models::ADBEmulatorCommand, +}; /// Emulator transport running on top on TCP. #[derive(Debug)] diff --git a/adb_client/src/error.rs b/adb_client/src/error.rs index 300ff00d..584b6f84 100644 --- a/adb_client/src/error.rs +++ b/adb_client/src/error.rs @@ -70,6 +70,8 @@ pub enum RustADBError { #[error("Cannot get home directory")] NoHomeDirectory, /// Generic USB error + #[cfg(feature = "rusb")] + #[cfg_attr(docsrs, doc(cfg(feature = "rusb")))] #[error("USB Error: {0}")] UsbError(#[from] rusb::Error), /// USB device not found @@ -88,6 +90,8 @@ pub enum RustADBError { #[error(transparent)] Base64EncodeError(#[from] base64::EncodeSliceError), /// An error occurred with RSA engine + #[cfg(any(feature = "rusb", feature = "webusb"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "rusb", feature = "webusb"))))] #[error(transparent)] RSAError(#[from] rsa::errors::Error), /// Cannot convert given data from slice @@ -97,6 +101,8 @@ pub enum RustADBError { #[error("wrong file extension: {0}")] WrongFileExtension(String), /// An error occurred with PKCS8 data + #[cfg(any(feature = "rusb", feature = "webusb"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "rusb", feature = "webusb"))))] #[error("error with pkcs8: {0}")] RsaPkcs8Error(#[from] rsa::pkcs8::Error), /// Error during certificate generation @@ -115,11 +121,13 @@ pub enum RustADBError { #[error("upgrade error: {0}")] UpgradeError(String), /// An error occurred while getting mdns devices + #[cfg(feature = "mdns")] + #[cfg_attr(docsrs, doc(cfg(feature = "mdns")))] #[error(transparent)] MDNSError(#[from] mdns_sd::Error), /// An error occurred while sending data to channel - #[error(transparent)] - SendError(#[from] std::sync::mpsc::SendError), + #[error("error sending data to channel")] + SendError, /// An unknown transport has been provided #[error("unknown transport: {0}")] UnknownTransport(String), diff --git a/adb_client/src/lib.rs b/adb_client/src/lib.rs index 8c883876..63a4cc39 100644 --- a/adb_client/src/lib.rs +++ b/adb_client/src/lib.rs @@ -3,25 +3,34 @@ #![forbid(missing_debug_implementations)] #![forbid(missing_docs)] #![doc = include_str!("../README.md")] +// Feature `doc_cfg` is currently only available on nightly. +// It is activated when cfg `docsrs` is enabled. +// Documentation can be build locally using: +// `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --no-deps --all-features` +#![cfg_attr(docsrs, feature(doc_cfg))] mod adb_device_ext; -mod constants; -mod device; -mod emulator_device; +mod adb_transport; +/// Emulator-related definitions +pub mod emulator; mod error; -mod mdns; +mod message_devices; mod models; -mod server; -mod server_device; -mod transports; + +/// Server-related definitions +pub mod server; + +/// Device reachable by the server related definitions +pub mod server_device; mod utils; +/// MDNS-related definitions +#[cfg(feature = "mdns")] +#[cfg_attr(docsrs, doc(cfg(feature = "mdns")))] +pub mod mdns; + pub use adb_device_ext::ADBDeviceExt; -pub use device::{ADBTcpDevice, ADBUSBDevice, is_adb_device, search_adb_devices}; -pub use emulator_device::ADBEmulatorDevice; +use adb_transport::ADBTransport; pub use error::{Result, RustADBError}; -pub use mdns::*; -pub use models::{AdbStatResponse, RebootType}; -pub use server::*; -pub use server_device::ADBServerDevice; -pub use transports::*; +pub use message_devices::*; +pub use models::{AdbStatResponse, HostFeatures, RebootType}; diff --git a/adb_client/src/mdns/mdns_discovery.rs b/adb_client/src/mdns/mdns_discovery.rs index d2ce76f8..771e98dd 100644 --- a/adb_client/src/mdns/mdns_discovery.rs +++ b/adb_client/src/mdns/mdns_discovery.rs @@ -1,7 +1,7 @@ use mdns_sd::{ServiceDaemon, ServiceEvent}; use std::{sync::mpsc::Sender, thread::JoinHandle}; -use crate::{MDNSDevice, Result, RustADBError}; +use crate::{Result, RustADBError, mdns::MDNSDevice}; const ADB_SERVICE_NAME: &str = "_adb-tls-connect._tcp.local."; @@ -44,9 +44,9 @@ impl MDNSDiscoveryService { // Ignoring these events. We are only interesting in found devices } ServiceEvent::ServiceResolved(service_info) => { - if let Err(e) = sender.send(MDNSDevice::from(service_info)) { - return Err(e.into()); - } + return sender + .send(MDNSDevice::from(service_info)) + .map_err(|_| RustADBError::SendError); } e => { log::warn!("received unknown event type {e:?}"); diff --git a/adb_client/src/device/adb_message_device.rs b/adb_client/src/message_devices/adb_message_device.rs similarity index 95% rename from adb_client/src/device/adb_message_device.rs rename to adb_client/src/message_devices/adb_message_device.rs index 68f51e51..fb26a83b 100644 --- a/adb_client/src/device/adb_message_device.rs +++ b/adb_client/src/message_devices/adb_message_device.rs @@ -1,14 +1,18 @@ -use super::{ADBRsaKey, ADBTransportMessage, MessageCommand, models::MessageSubcommand}; -use crate::device::adb_transport_message::{AUTH_RSAPUBLICKEY, AUTH_SIGNATURE, AUTH_TOKEN}; -use crate::{ADBMessageTransport, AdbStatResponse, Result, RustADBError, constants::BUFFER_SIZE}; +use crate::message_devices::adb_message_transport::ADBMessageTransport; +use crate::message_devices::adb_rsa_key::ADBRsaKey; +use crate::message_devices::adb_transport_message::ADBTransportMessage; +use crate::message_devices::message_commands::{MessageCommand, MessageSubcommand}; +use crate::message_devices::{AUTH_RSAPUBLICKEY, AUTH_SIGNATURE}; +use crate::{AUTH_TOKEN, AdbStatResponse, Result, RustADBError}; use bincode::config::{Configuration, Fixint, LittleEndian, NoLimit}; use byteorder::ReadBytesExt; -use rand::Rng; use serde::Serialize; use serde::de::DeserializeOwned; use std::io::{Cursor, Read, Seek}; use std::time::Duration; +const BUFFER_SIZE: usize = 65535; + const BINCODE_CONFIG: Configuration = bincode::config::legacy(); pub(crate) fn bincode_serialize_to_vec(val: E) -> Result> { @@ -284,11 +288,9 @@ impl ADBMessageDevice { } pub(crate) fn open_session(&mut self, data: &[u8]) -> Result { - let mut rng = rand::rng(); - let message = ADBTransportMessage::new( MessageCommand::Open, - rng.random(), // Our 'local-id' + rand::random::(), // Our 'local-id' 0, data, ); diff --git a/adb_client/src/device/adb_message_device_commands.rs b/adb_client/src/message_devices/adb_message_device_commands.rs similarity index 85% rename from adb_client/src/device/adb_message_device_commands.rs rename to adb_client/src/message_devices/adb_message_device_commands.rs index 994676e3..dbb59cc8 100644 --- a/adb_client/src/device/adb_message_device_commands.rs +++ b/adb_client/src/message_devices/adb_message_device_commands.rs @@ -1,11 +1,15 @@ -use crate::{ADBDeviceExt, ADBMessageTransport, RebootType, Result, models::AdbStatResponse}; +use crate::{ + ADBDeviceExt, RebootType, Result, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + }, + models::AdbStatResponse, +}; use std::{ io::{Read, Write}, path::Path, }; -use super::ADBMessageDevice; - impl ADBDeviceExt for ADBMessageDevice { fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { self.shell_command(command, output) diff --git a/adb_client/src/transports/traits/adb_message_transport.rs b/adb_client/src/message_devices/adb_message_transport.rs similarity index 90% rename from adb_client/src/transports/traits/adb_message_transport.rs rename to adb_client/src/message_devices/adb_message_transport.rs index 67eefd5e..d56b12ad 100644 --- a/adb_client/src/transports/traits/adb_message_transport.rs +++ b/adb_client/src/message_devices/adb_message_transport.rs @@ -1,7 +1,9 @@ use std::time::Duration; -use super::ADBTransport; -use crate::{Result, device::ADBTransportMessage}; +use crate::{ + Result, adb_transport::ADBTransport, + message_devices::adb_transport_message::ADBTransportMessage, +}; const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(u64::MAX); const DEFAULT_WRITE_TIMEOUT: Duration = Duration::from_secs(2); diff --git a/adb_client/src/device/models/adb_rsa_key.rs b/adb_client/src/message_devices/adb_rsa_key.rs similarity index 100% rename from adb_client/src/device/models/adb_rsa_key.rs rename to adb_client/src/message_devices/adb_rsa_key.rs diff --git a/adb_client/src/device/adb_transport_message.rs b/adb_client/src/message_devices/adb_transport_message.rs similarity index 93% rename from adb_client/src/device/adb_transport_message.rs rename to adb_client/src/message_devices/adb_transport_message.rs index 80647760..90597652 100644 --- a/adb_client/src/device/adb_transport_message.rs +++ b/adb_client/src/message_devices/adb_transport_message.rs @@ -1,12 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::{Result, RustADBError, device::adb_message_device}; - -use super::models::MessageCommand; - -pub const AUTH_TOKEN: u32 = 1; -pub const AUTH_SIGNATURE: u32 = 2; -pub const AUTH_RSAPUBLICKEY: u32 = 3; +use crate::{ + Result, RustADBError, + message_devices::{adb_message_device, message_commands::MessageCommand}, +}; #[derive(Debug)] pub struct ADBTransportMessage { diff --git a/adb_client/src/device/commands/framebuffer.rs b/adb_client/src/message_devices/commands/framebuffer.rs similarity index 94% rename from adb_client/src/device/commands/framebuffer.rs rename to adb_client/src/message_devices/commands/framebuffer.rs index 48b65073..594a5b11 100644 --- a/adb_client/src/device/commands/framebuffer.rs +++ b/adb_client/src/message_devices/commands/framebuffer.rs @@ -4,8 +4,11 @@ use byteorder::{LittleEndian, ReadBytesExt}; use image::{ImageBuffer, Rgba}; use crate::{ - ADBMessageTransport, Result, RustADBError, - device::{MessageCommand, adb_message_device::ADBMessageDevice}, + Result, RustADBError, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + message_commands::MessageCommand, + }, models::{FrameBufferInfoV1, FrameBufferInfoV2}, }; diff --git a/adb_client/src/device/commands/install.rs b/adb_client/src/message_devices/commands/install.rs similarity index 87% rename from adb_client/src/device/commands/install.rs rename to adb_client/src/message_devices/commands/install.rs index 3e25b625..b3abb4e6 100644 --- a/adb_client/src/device/commands/install.rs +++ b/adb_client/src/message_devices/commands/install.rs @@ -1,8 +1,11 @@ use std::{fs::File, path::Path}; use crate::{ - ADBMessageTransport, Result, - device::{MessageWriter, adb_message_device::ADBMessageDevice}, + Result, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + commands::utils::MessageWriter, + }, utils::check_extension_is_apk, }; diff --git a/adb_client/src/device/commands/mod.rs b/adb_client/src/message_devices/commands/mod.rs similarity index 89% rename from adb_client/src/device/commands/mod.rs rename to adb_client/src/message_devices/commands/mod.rs index d1d3de3c..f1d698ea 100644 --- a/adb_client/src/device/commands/mod.rs +++ b/adb_client/src/message_devices/commands/mod.rs @@ -6,3 +6,4 @@ mod reboot; mod shell; mod stat; mod uninstall; +mod utils; diff --git a/adb_client/src/device/commands/pull.rs b/adb_client/src/message_devices/commands/pull.rs similarity index 87% rename from adb_client/src/device/commands/pull.rs rename to adb_client/src/message_devices/commands/pull.rs index c8630bea..fcec9057 100644 --- a/adb_client/src/device/commands/pull.rs +++ b/adb_client/src/message_devices/commands/pull.rs @@ -1,11 +1,12 @@ use std::io::Write; use crate::{ - ADBMessageTransport, Result, RustADBError, - device::{ - ADBTransportMessage, MessageCommand, + Result, RustADBError, + message_devices::{ adb_message_device::{self, ADBMessageDevice}, - models::MessageSubcommand, + adb_message_transport::ADBMessageTransport, + adb_transport_message::ADBTransportMessage, + message_commands::{MessageCommand, MessageSubcommand}, }, }; diff --git a/adb_client/src/device/commands/push.rs b/adb_client/src/message_devices/commands/push.rs similarity index 82% rename from adb_client/src/device/commands/push.rs rename to adb_client/src/message_devices/commands/push.rs index 84d7f49c..8fd8cfaa 100644 --- a/adb_client/src/device/commands/push.rs +++ b/adb_client/src/message_devices/commands/push.rs @@ -1,10 +1,12 @@ use std::io::Read; use crate::{ - ADBMessageTransport, Result, - device::{ - ADBTransportMessage, MessageCommand, MessageSubcommand, + Result, + message_devices::{ adb_message_device::{self, ADBMessageDevice}, + adb_message_transport::ADBMessageTransport, + adb_transport_message::ADBTransportMessage, + message_commands::{MessageCommand, MessageSubcommand}, }, }; diff --git a/adb_client/src/device/commands/reboot.rs b/adb_client/src/message_devices/commands/reboot.rs similarity index 66% rename from adb_client/src/device/commands/reboot.rs rename to adb_client/src/message_devices/commands/reboot.rs index 58438f2f..ce461174 100644 --- a/adb_client/src/device/commands/reboot.rs +++ b/adb_client/src/message_devices/commands/reboot.rs @@ -1,6 +1,9 @@ use crate::{ - ADBMessageTransport, RebootType, Result, - device::{MessageCommand, adb_message_device::ADBMessageDevice}, + RebootType, Result, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + message_commands::MessageCommand, + }, }; impl ADBMessageDevice { diff --git a/adb_client/src/device/commands/shell.rs b/adb_client/src/message_devices/commands/shell.rs similarity index 90% rename from adb_client/src/device/commands/shell.rs rename to adb_client/src/message_devices/commands/shell.rs index 895d9287..7c39a3c1 100644 --- a/adb_client/src/device/commands/shell.rs +++ b/adb_client/src/message_devices/commands/shell.rs @@ -1,10 +1,12 @@ use std::io::{ErrorKind, Read, Write}; -use crate::Result; -use crate::device::ShellMessageWriter; use crate::{ - ADBMessageTransport, RustADBError, - device::{ADBMessageDevice, ADBTransportMessage, MessageCommand}, + Result, RustADBError, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + adb_transport_message::ADBTransportMessage, commands::utils::ShellMessageWriter, + message_commands::MessageCommand, + }, }; impl ADBMessageDevice { diff --git a/adb_client/src/device/commands/stat.rs b/adb_client/src/message_devices/commands/stat.rs similarity index 69% rename from adb_client/src/device/commands/stat.rs rename to adb_client/src/message_devices/commands/stat.rs index 27619b09..7da5896b 100644 --- a/adb_client/src/device/commands/stat.rs +++ b/adb_client/src/message_devices/commands/stat.rs @@ -1,5 +1,8 @@ use crate::{ - ADBMessageTransport, AdbStatResponse, Result, device::adb_message_device::ADBMessageDevice, + AdbStatResponse, Result, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + }, }; impl ADBMessageDevice { diff --git a/adb_client/src/device/commands/uninstall.rs b/adb_client/src/message_devices/commands/uninstall.rs similarity index 81% rename from adb_client/src/device/commands/uninstall.rs rename to adb_client/src/message_devices/commands/uninstall.rs index d2304466..1af47fd8 100644 --- a/adb_client/src/device/commands/uninstall.rs +++ b/adb_client/src/message_devices/commands/uninstall.rs @@ -1,4 +1,9 @@ -use crate::{ADBMessageTransport, Result, device::adb_message_device::ADBMessageDevice}; +use crate::{ + Result, + message_devices::{ + adb_message_device::ADBMessageDevice, adb_message_transport::ADBMessageTransport, + }, +}; impl ADBMessageDevice { pub(crate) fn uninstall(&mut self, package_name: &str) -> Result<()> { diff --git a/adb_client/src/device/message_writer.rs b/adb_client/src/message_devices/commands/utils/message_writer.rs similarity index 88% rename from adb_client/src/device/message_writer.rs rename to adb_client/src/message_devices/commands/utils/message_writer.rs index 8345144d..ae5f53a6 100644 --- a/adb_client/src/device/message_writer.rs +++ b/adb_client/src/message_devices/commands/utils/message_writer.rs @@ -1,8 +1,9 @@ use std::io::{Error, ErrorKind, Result, Write}; -use crate::ADBMessageTransport; - -use super::{ADBTransportMessage, MessageCommand}; +use crate::message_devices::{ + adb_message_transport::ADBMessageTransport, adb_transport_message::ADBTransportMessage, + message_commands::MessageCommand, +}; /// [`Write`] trait implementation to hide underlying ADB protocol write logic. /// diff --git a/adb_client/src/message_devices/commands/utils/mod.rs b/adb_client/src/message_devices/commands/utils/mod.rs new file mode 100644 index 00000000..9a7fd27d --- /dev/null +++ b/adb_client/src/message_devices/commands/utils/mod.rs @@ -0,0 +1,4 @@ +mod message_writer; +mod shell_message_writer; +pub use message_writer::MessageWriter; +pub use shell_message_writer::ShellMessageWriter; diff --git a/adb_client/src/device/shell_message_writer.rs b/adb_client/src/message_devices/commands/utils/shell_message_writer.rs similarity index 85% rename from adb_client/src/device/shell_message_writer.rs rename to adb_client/src/message_devices/commands/utils/shell_message_writer.rs index 5533a1f4..48fe49aa 100644 --- a/adb_client/src/device/shell_message_writer.rs +++ b/adb_client/src/message_devices/commands/utils/shell_message_writer.rs @@ -1,8 +1,9 @@ use std::io::Write; -use crate::ADBMessageTransport; - -use super::{ADBTransportMessage, models::MessageCommand}; +use crate::message_devices::{ + adb_message_transport::ADBMessageTransport, adb_transport_message::ADBTransportMessage, + message_commands::MessageCommand, +}; /// [`Write`] trait implementation to hide underlying ADB protocol write logic for shell commands. pub struct ShellMessageWriter { diff --git a/adb_client/src/device/models/message_commands.rs b/adb_client/src/message_devices/message_commands.rs similarity index 100% rename from adb_client/src/device/models/message_commands.rs rename to adb_client/src/message_devices/message_commands.rs diff --git a/adb_client/src/message_devices/mod.rs b/adb_client/src/message_devices/mod.rs new file mode 100644 index 00000000..50e3922e --- /dev/null +++ b/adb_client/src/message_devices/mod.rs @@ -0,0 +1,17 @@ +/// USB-related definitions +pub mod usb; + +/// Device reachable over TCP related definition +pub mod tcp; + +pub(crate) const AUTH_TOKEN: u32 = 1; +pub(crate) const AUTH_SIGNATURE: u32 = 2; +pub(crate) const AUTH_RSAPUBLICKEY: u32 = 3; + +mod adb_message_device; +mod adb_message_device_commands; +mod adb_message_transport; +mod adb_rsa_key; +mod adb_transport_message; +mod commands; +mod message_commands; diff --git a/adb_client/src/message_devices/tcp/README.md b/adb_client/src/message_devices/tcp/README.md new file mode 100644 index 00000000..cc24b1fd --- /dev/null +++ b/adb_client/src/message_devices/tcp/README.md @@ -0,0 +1,13 @@ +# Examples + +## Get a shell from device + +```rust no_run +use std::net::{SocketAddr, IpAddr, Ipv4Addr}; +use adb_client::{tcp::ADBTcpDevice, ADBDeviceExt}; + +let device_ip = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 10)); +let device_port = 43210; +let mut device = ADBTcpDevice::new(SocketAddr::new(device_ip, device_port)).expect("cannot find device"); +device.shell(&mut std::io::stdin(), Box::new(std::io::stdout())); +``` diff --git a/adb_client/src/device/adb_tcp_device.rs b/adb_client/src/message_devices/tcp/adb_tcp_device.rs similarity index 89% rename from adb_client/src/device/adb_tcp_device.rs rename to adb_client/src/message_devices/tcp/adb_tcp_device.rs index 9f984c30..d97239b9 100644 --- a/adb_client/src/device/adb_tcp_device.rs +++ b/adb_client/src/message_devices/tcp/adb_tcp_device.rs @@ -2,11 +2,15 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::{io::Read, net::SocketAddr}; -use super::adb_message_device::ADBMessageDevice; -use super::models::MessageCommand; -use super::{ADBRsaKey, ADBTransportMessage, get_default_adb_key_path}; -use crate::device::adb_usb_device::read_adb_private_key; -use crate::{ADBDeviceExt, ADBMessageTransport, ADBTransport, Result, TcpTransport}; +use crate::message_devices::adb_message_device::ADBMessageDevice; +use crate::message_devices::adb_message_transport::ADBMessageTransport; +use crate::message_devices::adb_rsa_key::ADBRsaKey; +use crate::message_devices::adb_transport_message::ADBTransportMessage; +use crate::message_devices::message_commands::MessageCommand; +use crate::tcp::tcp_transport::TcpTransport; +use crate::usb::read_adb_private_key; +use crate::utils::get_default_adb_key_path; +use crate::{ADBDeviceExt, ADBTransport, Result}; /// Represent a device reached and available over USB. #[derive(Debug)] diff --git a/adb_client/src/message_devices/tcp/mod.rs b/adb_client/src/message_devices/tcp/mod.rs new file mode 100644 index 00000000..17b12674 --- /dev/null +++ b/adb_client/src/message_devices/tcp/mod.rs @@ -0,0 +1,6 @@ +#![doc = include_str!("./README.md")] + +mod adb_tcp_device; +mod tcp_transport; + +pub use adb_tcp_device::ADBTcpDevice; diff --git a/adb_client/src/transports/tcp_transport.rs b/adb_client/src/message_devices/tcp/tcp_transport.rs similarity index 97% rename from adb_client/src/transports/tcp_transport.rs rename to adb_client/src/message_devices/tcp/tcp_transport.rs index d420b28b..09fa2f92 100644 --- a/adb_client/src/transports/tcp_transport.rs +++ b/adb_client/src/message_devices/tcp/tcp_transport.rs @@ -5,12 +5,15 @@ use rustls::{ pki_types::{CertificateDer, PrivatePkcs8KeyDer, pem::PemObject}, }; -use super::{ADBMessageTransport, ADBTransport}; use crate::{ Result, RustADBError, - device::{ - ADBTransportMessage, ADBTransportMessageHeader, MessageCommand, get_default_adb_key_path, + adb_transport::ADBTransport, + message_devices::{ + adb_message_transport::ADBMessageTransport, + adb_transport_message::{ADBTransportMessage, ADBTransportMessageHeader}, + message_commands::MessageCommand, }, + utils::get_default_adb_key_path, }; use std::{ fs::read_to_string, @@ -76,7 +79,7 @@ impl Write for CurrentConnection { } } -/// Transport running on USB +/// Transport running on TCP #[derive(Clone, Debug)] pub struct TcpTransport { address: SocketAddr, @@ -207,7 +210,7 @@ impl ADBMessageTransport for TcpTransport { fn read_message_with_timeout( &mut self, read_timeout: std::time::Duration, - ) -> Result { + ) -> Result { let raw_connection_lock = self.get_current_connection()?; let mut raw_connection = raw_connection_lock.lock()?; diff --git a/adb_client/src/message_devices/usb/README.md b/adb_client/src/message_devices/usb/README.md new file mode 100644 index 00000000..c1414c1e --- /dev/null +++ b/adb_client/src/message_devices/usb/README.md @@ -0,0 +1,26 @@ +# Examples + +## Launch a command on device + +```rust no_run +use adb_client::{usb::ADBUSBDevice, ADBDeviceExt}; + +let vendor_id = 0x04e8; +let product_id = 0x6860; +let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device"); +device.shell_command(&["df", "-h"], &mut std::io::stdout()); +``` + +## Push a file to the device + +```rust no_run +use adb_client::{usb::ADBUSBDevice, ADBDeviceExt}; +use std::fs::File; +use std::path::Path; + +let vendor_id = 0x04e8; +let product_id = 0x6860; +let mut device = ADBUSBDevice::new(vendor_id, product_id).expect("cannot find device"); +let mut input = File::open("/tmp/file.txt").expect("Cannot open file"); +device.push(&mut input, &"/data/local/tmp"); +``` diff --git a/adb_client/src/message_devices/usb/adb_rusb_device.rs b/adb_client/src/message_devices/usb/adb_rusb_device.rs new file mode 100644 index 00000000..fb99b287 --- /dev/null +++ b/adb_client/src/message_devices/usb/adb_rusb_device.rs @@ -0,0 +1,100 @@ +use std::{ + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +use crate::{ + ADBDeviceExt, Result, RustADBError, + usb::{RusbTransport, adb_usb_device::ADBUSBDevice, search_adb_devices}, + utils::get_default_adb_key_path, +}; + +/// Implement Android USB device reachable over wired USB +#[derive(Debug)] +pub struct ADBRusbDevice { + inner: ADBUSBDevice, +} + +impl ADBRusbDevice { + /// Instantiate a new [`ADBRusbDevice`] + 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 [`ADBRusbDevice`] using a custom private key path + pub fn new_with_custom_private_key( + vendor_id: u16, + product_id: u16, + private_key_path: PathBuf, + ) -> Result { + let transport = RusbTransport::new(vendor_id, product_id)?; + + Ok(Self { + inner: ADBUSBDevice::new_from_transport(transport, Some(private_key_path))?, + }) + } + + /// 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)) => { + ADBRusbDevice::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(), + )), + } + } +} + +impl ADBDeviceExt for ADBRusbDevice { + #[inline] + fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { + self.inner.shell_command(command, output) + } + + #[inline] + fn shell<'a>(&mut self, reader: &mut dyn Read, writer: Box) -> Result<()> { + self.inner.shell(reader, writer) + } + + #[inline] + fn stat(&mut self, remote_path: &str) -> Result { + self.inner.stat(remote_path) + } + + #[inline] + fn pull(&mut self, source: &dyn AsRef, output: &mut dyn Write) -> Result<()> { + self.inner.pull(source, output) + } + + #[inline] + fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { + self.inner.push(stream, path) + } + + #[inline] + fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { + self.inner.reboot(reboot_type) + } + + #[inline] + fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { + self.inner.install(apk_path) + } + + #[inline] + fn uninstall(&mut self, package: &str) -> Result<()> { + self.inner.uninstall(package) + } + + #[inline] + fn framebuffer_inner(&mut self) -> Result, Vec>> { + self.inner.framebuffer_inner() + } +} diff --git a/adb_client/src/message_devices/usb/adb_usb_device.rs b/adb_client/src/message_devices/usb/adb_usb_device.rs new file mode 100644 index 00000000..27eb148f --- /dev/null +++ b/adb_client/src/message_devices/usb/adb_usb_device.rs @@ -0,0 +1,187 @@ +use std::io::Read; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +use crate::ADBDeviceExt; +use crate::AUTH_TOKEN; +use crate::Result; +use crate::RustADBError; +use crate::message_devices::AUTH_RSAPUBLICKEY; +use crate::message_devices::AUTH_SIGNATURE; +use crate::message_devices::adb_message_device::ADBMessageDevice; +use crate::message_devices::adb_message_transport::ADBMessageTransport; +use crate::message_devices::adb_rsa_key::ADBRsaKey; +use crate::message_devices::adb_transport_message::ADBTransportMessage; +use crate::message_devices::message_commands::MessageCommand; +use crate::usb::read_adb_private_key; +use crate::utils::get_default_adb_key_path; + +/// Private struct implementing Android USB device logic, depending on a `ADBMessageTransport`. +#[derive(Debug)] +pub(crate) struct ADBUSBDevice { + private_key: ADBRsaKey, + inner: ADBMessageDevice, +} + +impl ADBUSBDevice { + /// Instantiate a new [`ADBUSBDevice`] from a [`RusbTransport`] and an optional private key path. + pub fn new_from_transport(transport: T, private_key_path: Option) -> Result { + 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: T, private_key_path: &PathBuf) -> Result { + let private_key = if let Some(private_key) = read_adb_private_key(private_key_path)? { + private_key + } else { + log::warn!( + "No private key found at path {}. Using a temporary random one.", + private_key_path.display() + ); + ADBRsaKey::new_random()? + }; + + let mut s = Self { + private_key, + inner: ADBMessageDevice::new(transport), + }; + + s.connect()?; + + Ok(s) + } + + /// Send initial connect + pub fn connect(&mut self) -> Result<()> { + self.get_transport_mut().connect()?; + + let message = ADBTransportMessage::new( + MessageCommand::Cnxn, + 0x0100_0000, + 1_048_576, + format!("host::{}\0", env!("CARGO_PKG_NAME")).as_bytes(), + ); + + self.get_transport_mut().write_message(message)?; + + let message = self.get_transport_mut().read_message()?; + // If the device returned CNXN instead of AUTH it does not require authentication, + // so we can skip the auth steps. + if message.header().command() == MessageCommand::Cnxn { + return Ok(()); + } + message.assert_command(MessageCommand::Auth)?; + + // At this point, we should have receive an AUTH message with arg0 == 1 + let auth_message = match message.header().arg0() { + AUTH_TOKEN => message, + v => { + return Err(RustADBError::ADBRequestFailed(format!( + "Received AUTH message with type != 1 ({v})" + ))); + } + }; + + let sign = self.private_key.sign(auth_message.into_payload())?; + + let message = ADBTransportMessage::new(MessageCommand::Auth, AUTH_SIGNATURE, 0, &sign); + + self.get_transport_mut().write_message(message)?; + + let received_response = self.get_transport_mut().read_message()?; + + if received_response.header().command() == MessageCommand::Cnxn { + log::info!( + "Authentication OK, device info {}", + String::from_utf8(received_response.into_payload())? + ); + return Ok(()); + } + + let mut pubkey = self.private_key.android_pubkey_encode()?.into_bytes(); + pubkey.push(b'\0'); + + let message = ADBTransportMessage::new(MessageCommand::Auth, AUTH_RSAPUBLICKEY, 0, &pubkey); + + self.get_transport_mut().write_message(message)?; + + let response = self + .get_transport_mut() + .read_message_with_timeout(Duration::from_secs(10)) + .and_then(|message| { + message.assert_command(MessageCommand::Cnxn)?; + Ok(message) + })?; + + log::info!( + "Authentication OK, device info {}", + String::from_utf8(response.into_payload())? + ); + + Ok(()) + } + + #[inline] + pub(crate) fn get_transport_mut(&mut self) -> &mut T { + self.inner.get_transport_mut() + } +} + +impl ADBDeviceExt for ADBUSBDevice { + #[inline] + fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { + self.inner.shell_command(command, output) + } + + #[inline] + fn shell<'a>(&mut self, reader: &mut dyn Read, writer: Box) -> Result<()> { + self.inner.shell(reader, writer) + } + + #[inline] + fn stat(&mut self, remote_path: &str) -> Result { + self.inner.stat(remote_path) + } + + #[inline] + fn pull(&mut self, source: &dyn AsRef, output: &mut dyn Write) -> Result<()> { + self.inner.pull(source, output) + } + + #[inline] + fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { + self.inner.push(stream, path) + } + + #[inline] + fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { + self.inner.reboot(reboot_type) + } + + #[inline] + fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { + self.inner.install(apk_path) + } + + #[inline] + fn uninstall(&mut self, package: &str) -> Result<()> { + self.inner.uninstall(package) + } + + #[inline] + fn framebuffer_inner(&mut self) -> Result, Vec>> { + self.inner.framebuffer_inner() + } +} + +impl Drop for ADBUSBDevice { + fn drop(&mut self) { + // Best effort here + let _ = self.get_transport_mut().disconnect(); + } +} diff --git a/adb_client/src/message_devices/usb/adb_webusb_device.rs b/adb_client/src/message_devices/usb/adb_webusb_device.rs new file mode 100644 index 00000000..a2903704 --- /dev/null +++ b/adb_client/src/message_devices/usb/adb_webusb_device.rs @@ -0,0 +1,69 @@ +use std::{ + io::{Read, Write}, + path::Path, +}; + +use crate::{ + ADBDeviceExt, Result, + usb::{WebUsbTransport, adb_usb_device::ADBUSBDevice}, +}; + +/// Implement Android USB device reachable over wired USB +#[derive(Debug)] +pub struct ADBWebUsbDevice { + inner: ADBUSBDevice, +} + +impl ADBWebUsbDevice { + /// Instantiate a new [`ADBRusb`] + pub fn new(_vendor_id: u16, _product_id: u16) -> Result { + todo!() + } +} + +impl ADBDeviceExt for ADBWebUsbDevice { + #[inline] + fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { + self.inner.shell_command(command, output) + } + + #[inline] + fn shell<'a>(&mut self, reader: &mut dyn Read, writer: Box) -> Result<()> { + self.inner.shell(reader, writer) + } + + #[inline] + fn stat(&mut self, remote_path: &str) -> Result { + self.inner.stat(remote_path) + } + + #[inline] + fn pull(&mut self, source: &dyn AsRef, output: &mut dyn Write) -> Result<()> { + self.inner.pull(source, output) + } + + #[inline] + fn push(&mut self, stream: &mut dyn Read, path: &dyn AsRef) -> Result<()> { + self.inner.push(stream, path) + } + + #[inline] + fn reboot(&mut self, reboot_type: crate::RebootType) -> Result<()> { + self.inner.reboot(reboot_type) + } + + #[inline] + fn install(&mut self, apk_path: &dyn AsRef) -> Result<()> { + self.inner.install(apk_path) + } + + #[inline] + fn uninstall(&mut self, package: &str) -> Result<()> { + self.inner.uninstall(package) + } + + #[inline] + fn framebuffer_inner(&mut self) -> Result, Vec>> { + self.inner.framebuffer_inner() + } +} diff --git a/adb_client/src/message_devices/usb/backends/mod.rs b/adb_client/src/message_devices/usb/backends/mod.rs new file mode 100644 index 00000000..57d40bbd --- /dev/null +++ b/adb_client/src/message_devices/usb/backends/mod.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "rusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusb")))] +pub mod rusb_transport; + +#[cfg(feature = "webusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "webusb")))] +pub mod webusb_transport; diff --git a/adb_client/src/transports/usb_transport.rs b/adb_client/src/message_devices/usb/backends/rusb_transport.rs similarity index 92% rename from adb_client/src/transports/usb_transport.rs rename to adb_client/src/message_devices/usb/backends/rusb_transport.rs index 5310486d..da5accd0 100644 --- a/adb_client/src/transports/usb_transport.rs +++ b/adb_client/src/message_devices/usb/backends/rusb_transport.rs @@ -5,10 +5,15 @@ use rusb::{ constants::LIBUSB_CLASS_VENDOR_SPEC, }; -use super::{ADBMessageTransport, ADBTransport}; use crate::{ Result, RustADBError, - device::{ADBTransportMessage, ADBTransportMessageHeader, MessageCommand}, + adb_transport::ADBTransport, + message_devices::{ + adb_message_transport::ADBMessageTransport, + adb_transport_message::{ADBTransportMessage, ADBTransportMessageHeader}, + message_commands::MessageCommand, + }, + usb::constants::class_codes::ADB_SUBCLASS, }; #[derive(Clone, Debug)] @@ -18,18 +23,18 @@ struct Endpoint { max_packet_size: usize, } -/// Transport running on USB +/// Transport running on USB using `rusb` as a backend. #[derive(Debug, Clone)] -pub struct USBTransport { +pub struct RusbTransport { device: Device, handle: Option>>, read_endpoint: Option, write_endpoint: Option, } -impl USBTransport { - /// Instantiate a new [`USBTransport`]. - /// Only the first device with given `vendor_id` and `product_id` is returned. +impl RusbTransport { + /// Instantiate a new [`RusbTransport`]. + /// Only the first device with given vendor_id and product_id is returned. pub fn new(vendor_id: u16, product_id: u16) -> Result { for device in rusb::devices()?.iter() { if let Ok(descriptor) = device.device_descriptor() { @@ -44,7 +49,7 @@ impl USBTransport { ))) } - /// Instantiate a new [`USBTransport`] from a [`rusb::Device`]. + /// Instantiate a new [`RusbTransport`] 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) -> Self { @@ -104,7 +109,7 @@ impl USBTransport { for endpoint_desc in interface_desc.endpoint_descriptors() { if endpoint_desc.transfer_type() == TransferType::Bulk && interface_desc.class_code() == LIBUSB_CLASS_VENDOR_SPEC - && interface_desc.sub_class_code() == 0x42 + && interface_desc.sub_class_code() == ADB_SUBCLASS && interface_desc.protocol_code() == 0x01 { let endpoint = Endpoint { @@ -159,7 +164,7 @@ impl USBTransport { } } -impl ADBTransport for USBTransport { +impl ADBTransport for RusbTransport { fn connect(&mut self) -> crate::Result<()> { let device = self.device.open()?; @@ -198,7 +203,7 @@ impl ADBTransport for USBTransport { } } -impl ADBMessageTransport for USBTransport { +impl ADBMessageTransport for RusbTransport { fn write_message_with_timeout( &mut self, message: ADBTransportMessage, diff --git a/adb_client/src/message_devices/usb/backends/webusb_transport.rs b/adb_client/src/message_devices/usb/backends/webusb_transport.rs new file mode 100644 index 00000000..0e87cb85 --- /dev/null +++ b/adb_client/src/message_devices/usb/backends/webusb_transport.rs @@ -0,0 +1,48 @@ +use std::time::Duration; + +use crate::{ + Result, + adb_transport::ADBTransport, + message_devices::{ + adb_message_transport::ADBMessageTransport, adb_transport_message::ADBTransportMessage, + }, +}; + +/// Transport running on USB using `webusb` as a backend. +#[derive(Clone, Debug)] +pub struct WebUsbTransport {} + +impl WebUsbTransport { + /// Instantiate a new [`WebUsbTransport`]. + /// Only the first device with given vendor_id and product_id is returned. + pub fn new() -> Self { + WebUsbTransport {} + } +} + +impl ADBTransport for WebUsbTransport { + fn connect(&mut self) -> crate::Result<()> { + todo!() + } + + fn disconnect(&mut self) -> crate::Result<()> { + todo!() + } +} + +impl ADBMessageTransport for WebUsbTransport { + fn read_message_with_timeout( + &mut self, + _read_timeout: Duration, + ) -> Result { + todo!() + } + + fn write_message_with_timeout( + &mut self, + message: ADBTransportMessage, + write_timeout: std::time::Duration, + ) -> Result<()> { + todo!() + } +} diff --git a/adb_client/src/message_devices/usb/mod.rs b/adb_client/src/message_devices/usb/mod.rs new file mode 100644 index 00000000..dbc60db9 --- /dev/null +++ b/adb_client/src/message_devices/usb/mod.rs @@ -0,0 +1,78 @@ +#![doc = include_str!("./README.md")] + +/// Common USB constants for Android Debug Bridge +pub mod constants { + /// Standard Android vendor ID + pub const ANDROID_VENDOR_ID: u16 = 0x18d1; + + /// Common ADB product IDs + pub mod product_ids { + /// ADB interface + pub const ADB: u16 = 0x4ee7; + /// ADB + MTP + pub const ADB_MTP: u16 = 0x4ee2; + /// ADB + RNDIS + pub const ADB_RNDIS: u16 = 0x4ee4; + /// Fastboot interface + pub const FASTBOOT: u16 = 0x4ee0; + } + + /// USB class codes for ADB detection + pub mod class_codes { + /// ADB subclass code + pub const ADB_SUBCLASS: u8 = 0x42; + /// ADB protocol code + pub const ADB_PROTOCOL: u8 = 0x1; + /// Bulk transfer class + pub const BULK_CLASS: u8 = 0xdc; + /// Bulk ADB subclass + pub const BULK_ADB_SUBCLASS: u8 = 2; + } +} + +// ################################################### +// rusb specific modules +#[cfg(feature = "rusb")] +mod adb_rusb_device; + +// Device implementations +#[cfg(feature = "rusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusb")))] +pub use adb_rusb_device::ADBRusbDevice; + +// Transport implementations +#[cfg(feature = "rusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusb")))] +pub use backends::rusb_transport::RusbTransport; +// ################################################### + +// ################################################### +// webusb specific modules +#[cfg(feature = "webusb")] +mod adb_webusb_device; + +// Device implementations +#[cfg(feature = "webusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "webusb")))] +pub use adb_webusb_device::ADBWebUsbDevice; + +// Transport implementations +#[cfg(feature = "webusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "webusb")))] +pub use backends::webusb_transport::WebUsbTransport; +// ################################################### + +mod backends; +mod utils; + +#[cfg(any(feature = "rusb", feature = "webusb"))] +mod adb_usb_device; + +// Utility functions +#[cfg(any(feature = "rusb", feature = "webusb"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "rusb", feature = "webusb"))))] +pub use utils::read_adb_private_key; + +#[cfg(feature = "rusb")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusb")))] +pub use utils::{is_adb_device, search_adb_devices}; diff --git a/adb_client/src/message_devices/usb/utils.rs b/adb_client/src/message_devices/usb/utils.rs new file mode 100644 index 00000000..aa4aadcd --- /dev/null +++ b/adb_client/src/message_devices/usb/utils.rs @@ -0,0 +1,98 @@ +//! USB utilities that are independent of specific transport implementations + +use crate::message_devices::adb_rsa_key::ADBRsaKey; +use crate::{Result, RustADBError}; +use std::fs::read_to_string; +use std::path::Path; + +#[cfg(feature = "rusb")] +use rusb::{Device, DeviceDescriptor, UsbContext}; + +#[cfg(feature = "rusb")] +use rusb::constants::LIBUSB_CLASS_VENDOR_SPEC; + +use crate::usb::constants::class_codes::{ + ADB_PROTOCOL, ADB_SUBCLASS, BULK_ADB_SUBCLASS, BULK_CLASS, +}; + +/// Read an ADB private key from a file path +/// +/// Returns `Ok(None)` if the file doesn't exist, `Ok(Some(key))` if the key was successfully loaded, +/// or an error if there was a problem reading the file. +pub fn read_adb_private_key>(private_key_path: P) -> Result> { + // Try to read the private key file from given path + // If the file is not found, return None + // If there is another error while reading the file, return this error + // Else, return the private key content + let pk = match read_to_string(private_key_path.as_ref()) { + Ok(pk) => pk, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(e) => return Err(e.into()), + }; + + match ADBRsaKey::new_from_pkcs8(&pk) { + Ok(pk) => Ok(Some(pk)), + Err(e) => Err(e), + } +} + +/// Search for ADB devices connected via USB +/// +/// Returns the vendor_id and product_id of the first ADB device found, +/// or `None` if no devices are found. +#[cfg(feature = "rusb")] +pub fn search_adb_devices() -> 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())); + } + } + + 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}", + ))), + } +} + +/// Check if a USB device is an ADB device +/// +/// This function inspects the device descriptor and configuration to determine +/// if it's an Android Debug Bridge device. +#[cfg(feature = "rusb")] +pub fn is_adb_device(device: &Device, des: &DeviceDescriptor) -> bool { + // Some devices require choosing the file transfer mode + // for usb debugging to take effect. + 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 == LIBUSB_CLASS_VENDOR_SPEC && subcl == ADB_SUBCLASS) + || (class == BULK_CLASS && subcl == BULK_ADB_SUBCLASS)) + { + return true; + } + } + } + } + + false +} diff --git a/adb_client/src/models/host_features.rs b/adb_client/src/models/host_features.rs index 85fc22d7..25db03ad 100644 --- a/adb_client/src/models/host_features.rs +++ b/adb_client/src/models/host_features.rs @@ -1,8 +1,11 @@ use std::fmt::Display; +/// Available host features. #[derive(Debug, PartialEq)] pub enum HostFeatures { + /// Shell version 2. ShellV2, + /// Command. Cmd, } diff --git a/adb_client/src/models/mod.rs b/adb_client/src/models/mod.rs index 66f3cefc..5dbc0be3 100644 --- a/adb_client/src/models/mod.rs +++ b/adb_client/src/models/mod.rs @@ -1,15 +1,13 @@ mod adb_request_status; -mod adb_server_command; mod adb_stat_response; mod framebuffer_info; mod host_features; mod reboot_type; mod sync_command; -pub use adb_request_status::AdbRequestStatus; -pub(crate) use adb_server_command::AdbServerCommand; +pub(crate) use adb_request_status::AdbRequestStatus; pub use adb_stat_response::AdbStatResponse; pub(crate) use framebuffer_info::{FrameBufferInfoV1, FrameBufferInfoV2}; pub use host_features::HostFeatures; pub use reboot_type::RebootType; -pub use sync_command::SyncCommand; +pub(crate) use sync_command::SyncCommand; diff --git a/adb_client/src/server/README.md b/adb_client/src/server/README.md new file mode 100644 index 00000000..845da978 --- /dev/null +++ b/adb_client/src/server/README.md @@ -0,0 +1,15 @@ +# Examples + +## Get available ADB devices + +```rust no_run +use adb_client::server::ADBServer; +use std::net::{SocketAddrV4, Ipv4Addr}; + +// A custom server address can be provided +let server_ip = Ipv4Addr::new(127, 0, 0, 1); +let server_port = 5037; + +let mut server = ADBServer::new(SocketAddrV4::new(server_ip, server_port)); +server.devices(); +``` diff --git a/adb_client/src/server/adb_server.rs b/adb_client/src/server/adb_server.rs index 783f721c..ed23d7ff 100644 --- a/adb_client/src/server/adb_server.rs +++ b/adb_client/src/server/adb_server.rs @@ -1,7 +1,7 @@ use crate::ADBTransport; use crate::Result; use crate::RustADBError; -use crate::TCPServerTransport; +use crate::server::tcp_server_transport::TCPServerTransport; use std::collections::HashMap; use std::net::SocketAddrV4; use std::process::Command; diff --git a/adb_client/src/models/adb_server_command.rs b/adb_client/src/server/adb_server_command.rs similarity index 97% rename from adb_client/src/models/adb_server_command.rs rename to adb_client/src/server/adb_server_command.rs index 8790fbc4..337981bb 100644 --- a/adb_client/src/models/adb_server_command.rs +++ b/adb_client/src/server/adb_server_command.rs @@ -1,8 +1,10 @@ use std::fmt::Display; -use crate::{WaitForDeviceState, WaitForDeviceTransport}; +use crate::{ + RebootType, + server::{WaitForDeviceState, WaitForDeviceTransport}, +}; -use super::RebootType; use std::net::SocketAddrV4; pub(crate) enum AdbServerCommand { diff --git a/adb_client/src/server/commands/connect.rs b/adb_client/src/server/commands/connect.rs index 2dab4193..70ca941f 100644 --- a/adb_client/src/server/commands/connect.rs +++ b/adb_client/src/server/commands/connect.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, Result, RustADBError, models::AdbServerCommand}; +use crate::{ + Result, RustADBError, + server::{ADBServer, AdbServerCommand}, +}; use std::net::SocketAddrV4; impl ADBServer { diff --git a/adb_client/src/server/commands/devices.rs b/adb_client/src/server/commands/devices.rs index d930974a..4e173b37 100644 --- a/adb_client/src/server/commands/devices.rs +++ b/adb_client/src/server/commands/devices.rs @@ -1,8 +1,10 @@ use std::io::Read; use crate::{ - ADBEmulatorDevice, ADBServer, ADBServerDevice, DeviceLong, DeviceShort, Result, RustADBError, - models::AdbServerCommand, + Result, RustADBError, + emulator::ADBEmulatorDevice, + server::{ADBServer, AdbServerCommand, DeviceLong, DeviceShort}, + server_device::ADBServerDevice, }; impl ADBServer { diff --git a/adb_client/src/server/commands/disconnect.rs b/adb_client/src/server/commands/disconnect.rs index 2d4ea1e9..fdf072a6 100644 --- a/adb_client/src/server/commands/disconnect.rs +++ b/adb_client/src/server/commands/disconnect.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, Result, RustADBError, models::AdbServerCommand}; +use crate::{ + Result, RustADBError, + server::{ADBServer, AdbServerCommand}, +}; use std::net::SocketAddrV4; impl ADBServer { diff --git a/adb_client/src/server/commands/kill.rs b/adb_client/src/server/commands/kill.rs index 89f02f87..669665e5 100644 --- a/adb_client/src/server/commands/kill.rs +++ b/adb_client/src/server/commands/kill.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, Result, models::AdbServerCommand}; +use crate::{ + Result, + server::{ADBServer, AdbServerCommand}, +}; impl ADBServer { /// Asks the ADB server to quit immediately. diff --git a/adb_client/src/server/commands/mdns.rs b/adb_client/src/server/commands/mdns.rs index 05a8f28d..3be35ca8 100644 --- a/adb_client/src/server/commands/mdns.rs +++ b/adb_client/src/server/commands/mdns.rs @@ -1,7 +1,8 @@ use std::io::BufRead; use crate::{ - ADBServer, MDNSServices, Result, models::AdbServerCommand, server::models::MDNSBackend, + Result, + server::{ADBServer, AdbServerCommand, MDNSServices, models::MDNSBackend}, }; const OPENSCREEN_MDNS_BACKEND: &str = "ADB_MDNS_OPENSCREEN"; diff --git a/adb_client/src/server/commands/pair.rs b/adb_client/src/server/commands/pair.rs index bafbf7da..38d1de18 100644 --- a/adb_client/src/server/commands/pair.rs +++ b/adb_client/src/server/commands/pair.rs @@ -1,5 +1,7 @@ -use crate::models::AdbServerCommand; -use crate::{ADBServer, Result, RustADBError}; +use crate::{ + Result, RustADBError, + server::{ADBServer, AdbServerCommand}, +}; use std::net::SocketAddrV4; impl ADBServer { diff --git a/adb_client/src/server/commands/reconnect.rs b/adb_client/src/server/commands/reconnect.rs index aa24bbd7..042fa18b 100644 --- a/adb_client/src/server/commands/reconnect.rs +++ b/adb_client/src/server/commands/reconnect.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, Result, models::AdbServerCommand}; +use crate::{ + Result, + server::{ADBServer, AdbServerCommand}, +}; impl ADBServer { /// Reconnect the device diff --git a/adb_client/src/server/commands/server_status.rs b/adb_client/src/server/commands/server_status.rs index dcba402e..d7eb1585 100644 --- a/adb_client/src/server/commands/server_status.rs +++ b/adb_client/src/server/commands/server_status.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, Result, models::AdbServerCommand, server::models::ServerStatus}; +use crate::{ + Result, + server::{ADBServer, AdbServerCommand, models::ServerStatus}, +}; impl ADBServer { /// Check ADB server status diff --git a/adb_client/src/server/commands/version.rs b/adb_client/src/server/commands/version.rs index 8dd0a526..69ae1713 100644 --- a/adb_client/src/server/commands/version.rs +++ b/adb_client/src/server/commands/version.rs @@ -1,4 +1,7 @@ -use crate::{ADBServer, AdbVersion, Result, models::AdbServerCommand}; +use crate::{ + Result, + server::{ADBServer, AdbServerCommand, AdbVersion}, +}; impl ADBServer { /// Gets server's internal version number. diff --git a/adb_client/src/server/commands/wait_for_device.rs b/adb_client/src/server/commands/wait_for_device.rs index abef15cc..c89b0191 100644 --- a/adb_client/src/server/commands/wait_for_device.rs +++ b/adb_client/src/server/commands/wait_for_device.rs @@ -1,5 +1,6 @@ use crate::{ - ADBServer, Result, WaitForDeviceState, WaitForDeviceTransport, models::AdbServerCommand, + Result, + server::{ADBServer, AdbServerCommand, WaitForDeviceState, WaitForDeviceTransport}, }; impl ADBServer { diff --git a/adb_client/src/server/mod.rs b/adb_client/src/server/mod.rs index d55f1310..a47dfe27 100644 --- a/adb_client/src/server/mod.rs +++ b/adb_client/src/server/mod.rs @@ -1,6 +1,12 @@ +#![doc = include_str!("./README.md")] + mod adb_server; +mod adb_server_command; mod commands; mod models; +mod tcp_server_transport; pub use adb_server::ADBServer; +pub(crate) use adb_server_command::AdbServerCommand; pub use models::*; +pub use tcp_server_transport::TCPServerTransport; diff --git a/adb_client/src/server/models/device_long.rs b/adb_client/src/server/models/device_long.rs index 6b8c92da..a54ef8fc 100644 --- a/adb_client/src/server/models/device_long.rs +++ b/adb_client/src/server/models/device_long.rs @@ -2,7 +2,8 @@ use std::str::FromStr; use std::sync::LazyLock; use std::{fmt::Display, str}; -use crate::{DeviceState, RustADBError}; +use crate::RustADBError; +use crate::server::DeviceState; use regex::bytes::Regex; static DEVICES_LONG_REGEX: LazyLock = LazyLock::new(|| { diff --git a/adb_client/src/server/models/device_short.rs b/adb_client/src/server/models/device_short.rs index 493cd6ed..1759df00 100644 --- a/adb_client/src/server/models/device_short.rs +++ b/adb_client/src/server/models/device_short.rs @@ -1,7 +1,7 @@ use regex::bytes::Regex; use std::{fmt::Display, str::FromStr, sync::LazyLock}; -use crate::{DeviceState, RustADBError}; +use crate::{RustADBError, server::DeviceState}; static DEVICES_REGEX: LazyLock = LazyLock::new(|| Regex::new("^(\\S+)\t(\\w+)\n?$").expect("Cannot build devices regex")); diff --git a/adb_client/src/transports/tcp_server_transport.rs b/adb_client/src/server/tcp_server_transport.rs similarity index 98% rename from adb_client/src/transports/tcp_server_transport.rs rename to adb_client/src/server/tcp_server_transport.rs index 20cac72d..e78e69a7 100644 --- a/adb_client/src/transports/tcp_server_transport.rs +++ b/adb_client/src/server/tcp_server_transport.rs @@ -4,8 +4,9 @@ use std::str::FromStr; use byteorder::{ByteOrder, LittleEndian}; +use crate::ADBTransport; use crate::models::{AdbRequestStatus, SyncCommand}; -use crate::{ADBTransport, models::AdbServerCommand}; +use crate::server::AdbServerCommand; use crate::{Result, RustADBError}; const DEFAULT_SERVER_IP: Ipv4Addr = Ipv4Addr::LOCALHOST; diff --git a/adb_client/src/server_device/README.md b/adb_client/src/server_device/README.md new file mode 100644 index 00000000..c7bab3ed --- /dev/null +++ b/adb_client/src/server_device/README.md @@ -0,0 +1,25 @@ +# Examples + +## Launch a command on device + +```rust no_run +use adb_client::{server::ADBServer, ADBDeviceExt}; + +let mut server = ADBServer::default(); +let mut device = server.get_device().expect("cannot get device"); +device.shell_command(&["df", "-h"], &mut std::io::stdout()); +``` + +## Push a file to the device + +```rust no_run +use adb_client::server::ADBServer; +use std::net::Ipv4Addr; +use std::fs::File; +use std::path::Path; + +let mut server = ADBServer::default(); +let mut device = server.get_device().expect("cannot get device"); +let mut input = File::open("/tmp/file.txt").expect("Cannot open file"); +device.push(&mut input, "/data/local/tmp"); +``` diff --git a/adb_client/src/server_device/adb_server_device.rs b/adb_client/src/server_device/adb_server_device.rs index 89d0ae2a..136772ab 100644 --- a/adb_client/src/server_device/adb_server_device.rs +++ b/adb_client/src/server_device/adb_server_device.rs @@ -1,4 +1,7 @@ -use crate::{ADBTransport, Result, TCPServerTransport, models::AdbServerCommand}; +use crate::{ + ADBTransport, Result, + server::{AdbServerCommand, TCPServerTransport}, +}; use std::net::SocketAddrV4; /// Represents a device connected to the ADB server. diff --git a/adb_client/src/server_device/adb_server_device_commands.rs b/adb_client/src/server_device/adb_server_device_commands.rs index ca2c938c..62696c97 100644 --- a/adb_client/src/server_device/adb_server_device_commands.rs +++ b/adb_client/src/server_device/adb_server_device_commands.rs @@ -5,12 +5,14 @@ use std::{ use crate::{ ADBDeviceExt, Result, RustADBError, - constants::BUFFER_SIZE, - models::{AdbServerCommand, AdbStatResponse, HostFeatures}, + models::{AdbStatResponse, HostFeatures}, + server::AdbServerCommand, }; use super::ADBServerDevice; +const BUFFER_SIZE: usize = 65535; + impl ADBDeviceExt for ADBServerDevice { fn shell_command(&mut self, command: &[&str], output: &mut dyn Write) -> Result<()> { let supported_features = self.host_features()?; diff --git a/adb_client/src/server_device/commands/forward.rs b/adb_client/src/server_device/commands/forward.rs index ff571f3f..9bf1fb3c 100644 --- a/adb_client/src/server_device/commands/forward.rs +++ b/adb_client/src/server_device/commands/forward.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Forward socket connection diff --git a/adb_client/src/server_device/commands/framebuffer.rs b/adb_client/src/server_device/commands/framebuffer.rs index b3a6e9fc..5284226e 100644 --- a/adb_client/src/server_device/commands/framebuffer.rs +++ b/adb_client/src/server_device/commands/framebuffer.rs @@ -4,8 +4,10 @@ use byteorder::{LittleEndian, ReadBytesExt}; use image::{ImageBuffer, Rgba}; use crate::{ - ADBServerDevice, Result, RustADBError, - models::{AdbServerCommand, FrameBufferInfoV1, FrameBufferInfoV2}, + Result, RustADBError, + models::{FrameBufferInfoV1, FrameBufferInfoV2}, + server::AdbServerCommand, + server_device::ADBServerDevice, }; impl ADBServerDevice { diff --git a/adb_client/src/server_device/commands/host_features.rs b/adb_client/src/server_device/commands/host_features.rs index 428b7eb2..d01f74f6 100644 --- a/adb_client/src/server_device/commands/host_features.rs +++ b/adb_client/src/server_device/commands/host_features.rs @@ -1,6 +1,5 @@ use crate::{ - ADBServerDevice, Result, - models::{AdbServerCommand, HostFeatures}, + Result, models::HostFeatures, server::AdbServerCommand, server_device::ADBServerDevice, }; impl ADBServerDevice { diff --git a/adb_client/src/server_device/commands/install.rs b/adb_client/src/server_device/commands/install.rs index 29a77ef5..50e36101 100644 --- a/adb_client/src/server_device/commands/install.rs +++ b/adb_client/src/server_device/commands/install.rs @@ -1,7 +1,7 @@ use std::{fs::File, io::Read, path::Path}; use crate::{ - Result, models::AdbServerCommand, server_device::ADBServerDevice, utils::check_extension_is_apk, + Result, server::AdbServerCommand, server_device::ADBServerDevice, utils::check_extension_is_apk, }; impl ADBServerDevice { diff --git a/adb_client/src/server_device/commands/list.rs b/adb_client/src/server_device/commands/list.rs index abdfcc12..d11e4f6c 100644 --- a/adb_client/src/server_device/commands/list.rs +++ b/adb_client/src/server_device/commands/list.rs @@ -1,6 +1,5 @@ use crate::{ - ADBServerDevice, Result, - models::{AdbServerCommand, SyncCommand}, + Result, models::SyncCommand, server::AdbServerCommand, server_device::ADBServerDevice, }; use byteorder::{ByteOrder, LittleEndian}; use std::{ diff --git a/adb_client/src/server_device/commands/logcat.rs b/adb_client/src/server_device/commands/logcat.rs index 35e04ea9..b427a1af 100644 --- a/adb_client/src/server_device/commands/logcat.rs +++ b/adb_client/src/server_device/commands/logcat.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use crate::{ADBDeviceExt, ADBServerDevice, Result}; +use crate::{ADBDeviceExt, Result, server_device::ADBServerDevice}; struct LogFilter { writer: W, diff --git a/adb_client/src/server_device/commands/reboot.rs b/adb_client/src/server_device/commands/reboot.rs index 3829e837..a41b0b8f 100644 --- a/adb_client/src/server_device/commands/reboot.rs +++ b/adb_client/src/server_device/commands/reboot.rs @@ -1,7 +1,4 @@ -use crate::{ - ADBServerDevice, Result, - models::{AdbServerCommand, RebootType}, -}; +use crate::{Result, models::RebootType, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Reboots the device diff --git a/adb_client/src/server_device/commands/reconnect.rs b/adb_client/src/server_device/commands/reconnect.rs index 03e889ae..0c4abf78 100644 --- a/adb_client/src/server_device/commands/reconnect.rs +++ b/adb_client/src/server_device/commands/reconnect.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Reconnect device diff --git a/adb_client/src/server_device/commands/recv.rs b/adb_client/src/server_device/commands/recv.rs index ae4b9ddd..e31764eb 100644 --- a/adb_client/src/server_device/commands/recv.rs +++ b/adb_client/src/server_device/commands/recv.rs @@ -1,6 +1,5 @@ use crate::{ - ADBServerDevice, Result, constants, - models::{AdbServerCommand, SyncCommand}, + Result, models::SyncCommand, server::AdbServerCommand, server_device::ADBServerDevice, }; use byteorder::{LittleEndian, ReadBytesExt}; use std::io::{BufReader, BufWriter, Read, Write}; @@ -63,6 +62,8 @@ impl Read for ADBRecvCommandReader { } } +const BUFFER_SIZE: usize = 65535; + impl ADBServerDevice { /// Receives path to stream from the device. pub fn pull(&mut self, path: &dyn AsRef, stream: &mut dyn Write) -> Result<()> { @@ -92,8 +93,8 @@ impl ADBServerDevice { let reader = ADBRecvCommandReader::new(raw_connection); std::io::copy( - &mut BufReader::with_capacity(constants::BUFFER_SIZE, reader), - &mut BufWriter::with_capacity(constants::BUFFER_SIZE, output), + &mut BufReader::with_capacity(BUFFER_SIZE, reader), + &mut BufWriter::with_capacity(BUFFER_SIZE, output), )?; // Connection should've been left in SYNC mode by now diff --git a/adb_client/src/server_device/commands/reverse.rs b/adb_client/src/server_device/commands/reverse.rs index b7d06db7..85419180 100644 --- a/adb_client/src/server_device/commands/reverse.rs +++ b/adb_client/src/server_device/commands/reverse.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Reverse socket connection diff --git a/adb_client/src/server_device/commands/send.rs b/adb_client/src/server_device/commands/send.rs index 9cf8b578..f384e0b0 100644 --- a/adb_client/src/server_device/commands/send.rs +++ b/adb_client/src/server_device/commands/send.rs @@ -1,6 +1,8 @@ use crate::{ - ADBServerDevice, Result, RustADBError, constants, - models::{AdbRequestStatus, AdbServerCommand, SyncCommand}, + Result, RustADBError, + models::{AdbRequestStatus, SyncCommand}, + server::AdbServerCommand, + server_device::ADBServerDevice, }; use std::{ convert::TryInto, @@ -40,6 +42,8 @@ impl Write for ADBSendCommandWriter { } } +const BUFFER_SIZE: usize = 65535; + impl ADBServerDevice { /// Send stream to path on the device. pub fn push>(&mut self, stream: R, path: A) -> Result<()> { @@ -71,8 +75,8 @@ impl ADBServerDevice { let writer = ADBSendCommandWriter::new(raw_connection); std::io::copy( - &mut BufReader::with_capacity(constants::BUFFER_SIZE, input), - &mut BufWriter::with_capacity(constants::BUFFER_SIZE, writer), + &mut BufReader::with_capacity(BUFFER_SIZE, input), + &mut BufWriter::with_capacity(BUFFER_SIZE, writer), )?; // Copy is finished, we can now notify as finished diff --git a/adb_client/src/server_device/commands/stat.rs b/adb_client/src/server_device/commands/stat.rs index 3d4f4282..a0ea30d6 100644 --- a/adb_client/src/server_device/commands/stat.rs +++ b/adb_client/src/server_device/commands/stat.rs @@ -3,8 +3,8 @@ use std::io::{Read, Write}; use byteorder::{ByteOrder, LittleEndian}; use crate::{ - ADBServerDevice, Result, RustADBError, - models::{AdbServerCommand, AdbStatResponse, SyncCommand}, + AdbStatResponse, Result, RustADBError, models::SyncCommand, server::AdbServerCommand, + server_device::ADBServerDevice, }; impl ADBServerDevice { diff --git a/adb_client/src/server_device/commands/tcpip.rs b/adb_client/src/server_device/commands/tcpip.rs index 3cb01ed2..5f4d6c98 100644 --- a/adb_client/src/server_device/commands/tcpip.rs +++ b/adb_client/src/server_device/commands/tcpip.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Set adb daemon to tcp/ip mode diff --git a/adb_client/src/server_device/commands/transport.rs b/adb_client/src/server_device/commands/transport.rs index 25b6ccb4..29f429c8 100644 --- a/adb_client/src/server_device/commands/transport.rs +++ b/adb_client/src/server_device/commands/transport.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Asks ADB server to switch the connection to either the device or emulator connect to/running on the host. Will fail if there is more than one such device/emulator available. diff --git a/adb_client/src/server_device/commands/uninstall.rs b/adb_client/src/server_device/commands/uninstall.rs index 32b8f954..2f8e9efa 100644 --- a/adb_client/src/server_device/commands/uninstall.rs +++ b/adb_client/src/server_device/commands/uninstall.rs @@ -1,6 +1,6 @@ use std::io::Read; -use crate::{Result, models::AdbServerCommand, server_device::ADBServerDevice}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Uninstall a package from device diff --git a/adb_client/src/server_device/commands/usb.rs b/adb_client/src/server_device/commands/usb.rs index e3c0ce20..df711a30 100644 --- a/adb_client/src/server_device/commands/usb.rs +++ b/adb_client/src/server_device/commands/usb.rs @@ -1,4 +1,4 @@ -use crate::{ADBServerDevice, Result, models::AdbServerCommand}; +use crate::{Result, server::AdbServerCommand, server_device::ADBServerDevice}; impl ADBServerDevice { /// Set adb daemon to usb mode diff --git a/adb_client/src/server_device/mod.rs b/adb_client/src/server_device/mod.rs index c7830011..286c69d6 100644 --- a/adb_client/src/server_device/mod.rs +++ b/adb_client/src/server_device/mod.rs @@ -1,3 +1,5 @@ +#![doc = include_str!("./README.md")] + mod adb_server_device; mod adb_server_device_commands; mod commands; diff --git a/adb_client/src/transports/mod.rs b/adb_client/src/transports/mod.rs deleted file mode 100644 index 28e89a06..00000000 --- a/adb_client/src/transports/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod tcp_emulator_transport; -mod tcp_server_transport; -mod tcp_transport; -mod traits; -mod usb_transport; - -pub use tcp_emulator_transport::TCPEmulatorTransport; -pub use tcp_server_transport::TCPServerTransport; -pub use tcp_transport::TcpTransport; -pub use traits::{ADBMessageTransport, ADBTransport}; -pub use usb_transport::USBTransport; diff --git a/adb_client/src/transports/traits/mod.rs b/adb_client/src/transports/traits/mod.rs deleted file mode 100644 index e655c814..00000000 --- a/adb_client/src/transports/traits/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod adb_message_transport; -mod adb_transport; - -pub use adb_message_transport::ADBMessageTransport; -pub use adb_transport::ADBTransport; diff --git a/adb_client/src/utils.rs b/adb_client/src/utils.rs index d91041d7..93c8d486 100644 --- a/adb_client/src/utils.rs +++ b/adb_client/src/utils.rs @@ -1,4 +1,7 @@ -use std::{ffi::OsStr, path::Path}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; use crate::{Result, RustADBError}; @@ -16,3 +19,9 @@ pub fn check_extension_is_apk>(path: P) -> Result<()> { Ok(()) } + +pub fn get_default_adb_key_path() -> Result { + std::env::home_dir() + .map(|home| home.join(".android").join("adbkey")) + .ok_or(RustADBError::NoHomeDirectory) +} diff --git a/examples/mdns/Cargo.toml b/examples/mdns/Cargo.toml new file mode 100644 index 00000000..5a673b51 --- /dev/null +++ b/examples/mdns/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mdns" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true +rust-version.workspace = true + +[dependencies] +adb_client = { path = "../../adb_client", default-features = false, features = [ + "mdns", +] } diff --git a/examples/mdns/src/main.rs b/examples/mdns/src/main.rs new file mode 100644 index 00000000..16861c17 --- /dev/null +++ b/examples/mdns/src/main.rs @@ -0,0 +1,22 @@ +use std::sync::mpsc; +use std::time::Duration; + +use adb_client::mdns::{MDNSDevice, MDNSDiscoveryService}; + +fn main() -> Result<(), Box> { + println!("Starting mDNS device discovery..."); + + // Create a channel to receive discovered devices information + let (sender, receiver) = mpsc::channel::(); + + // Create and start the discovery service + let mut discovery = MDNSDiscoveryService::new()?; + discovery.start(sender)?; + + loop { + if let Ok(device) = receiver.recv_timeout(Duration::from_millis(100)) { + println!("Found device: {}", device.fullname); + println!(" Addresses: {:?}", device.addresses); + } + } +} diff --git a/pyadb_client/src/adb_server.rs b/pyadb_client/src/adb_server.rs index d3fb7066..73982955 100644 --- a/pyadb_client/src/adb_server.rs +++ b/pyadb_client/src/adb_server.rs @@ -1,6 +1,6 @@ use std::net::SocketAddrV4; -use adb_client::ADBServer; +use adb_client::server::ADBServer; use anyhow::Result; use pyo3::{PyResult, pyclass, pymethods}; use pyo3_stub_gen_derive::{gen_stub_pyclass, gen_stub_pymethods}; diff --git a/pyadb_client/src/adb_server_device.rs b/pyadb_client/src/adb_server_device.rs index e6fd207e..27f979b7 100644 --- a/pyadb_client/src/adb_server_device.rs +++ b/pyadb_client/src/adb_server_device.rs @@ -1,4 +1,4 @@ -use adb_client::{ADBDeviceExt, ADBServerDevice}; +use adb_client::{ADBDeviceExt, server_device::ADBServerDevice}; use anyhow::Result; use pyo3::{pyclass, pymethods}; use pyo3_stub_gen_derive::{gen_stub_pyclass, gen_stub_pymethods}; diff --git a/pyadb_client/src/adb_usb_device.rs b/pyadb_client/src/adb_usb_device.rs index ea3cf5e6..1af1915d 100644 --- a/pyadb_client/src/adb_usb_device.rs +++ b/pyadb_client/src/adb_usb_device.rs @@ -1,6 +1,6 @@ use std::{fs::File, path::PathBuf}; -use adb_client::{ADBDeviceExt, ADBUSBDevice}; +use adb_client::{ADBDeviceExt, usb::ADBRusbDevice}; use anyhow::Result; use pyo3::{pyclass, pymethods}; use pyo3_stub_gen_derive::{gen_stub_pyclass, gen_stub_pymethods}; @@ -8,7 +8,7 @@ use pyo3_stub_gen_derive::{gen_stub_pyclass, gen_stub_pymethods}; #[gen_stub_pyclass] #[pyclass] /// Represent a device directly reachable over USB. -pub struct PyADBUSBDevice(ADBUSBDevice); +pub struct PyADBUSBDevice(ADBRusbDevice); #[gen_stub_pymethods] #[pymethods] @@ -17,7 +17,7 @@ impl PyADBUSBDevice { /// Autodetect a device reachable over USB. /// This method raises an error if multiple devices or none are connected. pub fn autodetect() -> Result { - Ok(ADBUSBDevice::autodetect()?.into()) + Ok(ADBRusbDevice::autodetect()?.into()) } /// Run shell commands on device and return the output (stdout + stderr merged) @@ -51,8 +51,8 @@ impl PyADBUSBDevice { } } -impl From for PyADBUSBDevice { - fn from(value: ADBUSBDevice) -> Self { +impl From for PyADBUSBDevice { + fn from(value: ADBRusbDevice) -> Self { Self(value) } } diff --git a/pyadb_client/src/models/devices.rs b/pyadb_client/src/models/devices.rs index 3ace3ef5..3ede6771 100644 --- a/pyadb_client/src/models/devices.rs +++ b/pyadb_client/src/models/devices.rs @@ -1,4 +1,4 @@ -use adb_client::DeviceShort; +use adb_client::server::DeviceShort; use pyo3::{pyclass, pymethods}; use pyo3_stub_gen_derive::{gen_stub_pyclass, gen_stub_pymethods};