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