Adds host:track-devices command
- Also clean code and TCP socket reading
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "adb_client"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
description = "Rust ADB (Android Debug Bridge) client library"
|
||||
keywords = ["adb"]
|
||||
readme = "README.md"
|
||||
@@ -21,7 +21,8 @@ required-features = ["adbclient"]
|
||||
thiserror = { version = "1.0.30", default_features = false }
|
||||
|
||||
# Features needed by adb_client binary
|
||||
clap = { version = "3.0.1", default_features = false, features = ["std", "derive"], optional = true }
|
||||
clap = { version = "3.0.4", default_features = false, features = ["std", "derive"], optional = true }
|
||||
anyhow = { version = "1.0.52", default_features = false, features = ["std"], optional = true }
|
||||
|
||||
[features]
|
||||
adbclient = ["clap"]
|
||||
adbclient = ["clap", "anyhow"]
|
||||
21
README.md
21
README.md
@@ -1,2 +1,23 @@
|
||||
# adb_client - Rust ADB (Android Debug Bridge) client library
|
||||
|
||||
This crate is not affiliated with Android development core team.
|
||||
|
||||
## Rust crate
|
||||
|
||||
Simply add this to your `Cargo.toml`:
|
||||
```toml
|
||||
[dependencies]
|
||||
adb_client = "*"
|
||||
```
|
||||
|
||||
## Rust binary
|
||||
|
||||
You can install the lightweight adb binary by running the following command :
|
||||
```
|
||||
cargo install adb_client --features adbclient
|
||||
```
|
||||
|
||||
|
||||
https://developer.android.com/studio/command-line/adb
|
||||
|
||||
https://github.com/cstyan/adbDocumentation
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
use adb_client::{AdbCommandProvider, AdbTcpConnexion};
|
||||
use adb_client::{AdbCommandProvider, AdbTcpConnexion, Device};
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(about, version, author)]
|
||||
struct Args {
|
||||
#[clap(
|
||||
short = 'a',
|
||||
long = "address",
|
||||
help = "Sets the listening address of ADB server",
|
||||
default_value = "127.0.0.1"
|
||||
)]
|
||||
/// Sets the listening address of ADB server
|
||||
#[clap(short = 'a', long = "address", default_value = "127.0.0.1")]
|
||||
pub address: String,
|
||||
#[clap(
|
||||
short = 'p',
|
||||
long = "port",
|
||||
help = "Sets the listening port of ADB server",
|
||||
default_value = "5037"
|
||||
)]
|
||||
/// Sets the listening port of ADB server
|
||||
#[clap(short = 'p', long = "port", default_value = "5037")]
|
||||
pub port: u16,
|
||||
#[clap(subcommand)]
|
||||
pub command: Command,
|
||||
@@ -24,33 +17,54 @@ struct Args {
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
enum Command {
|
||||
/// Prints current ADB version
|
||||
Version,
|
||||
/// Asks ADB server to quit immediately
|
||||
Kill,
|
||||
/// List connected devices
|
||||
Devices {
|
||||
#[clap(short = 'l', long = "long")]
|
||||
long: bool,
|
||||
},
|
||||
/// Tracks new devices showing up
|
||||
TrackDevices,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
let opt = Args::parse();
|
||||
|
||||
let connexion = AdbTcpConnexion::new()
|
||||
.address(opt.address)
|
||||
.unwrap()
|
||||
.port(opt.port);
|
||||
let connexion = AdbTcpConnexion::new().address(opt.address)?.port(opt.port);
|
||||
|
||||
match opt.command {
|
||||
Command::Version => {
|
||||
connexion.version().unwrap();
|
||||
let version = connexion.version()?;
|
||||
|
||||
println!("Android Debug Bridge version {}", version);
|
||||
println!("Package version {}-rust", std::env!("CARGO_PKG_VERSION"));
|
||||
}
|
||||
Command::Kill => {
|
||||
connexion.kill()?;
|
||||
}
|
||||
Command::Devices { long } => {
|
||||
if long {
|
||||
connexion.devices_long().unwrap();
|
||||
println!("List of devices attached (long)");
|
||||
connexion.devices_long()?;
|
||||
} else {
|
||||
for device in connexion.devices().unwrap() {
|
||||
println!("List of devices attached");
|
||||
for device in connexion.devices()? {
|
||||
println!("{}\t{}", device.identifier, device.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::TrackDevices => {
|
||||
let callback = |device: Device| {
|
||||
println!("{}", device);
|
||||
Ok(())
|
||||
};
|
||||
println!("Live list of devices attached");
|
||||
connexion.track_devices(callback)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::{Ipv4Addr, SocketAddrV4, TcpStream},
|
||||
str,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
models::{AdbCommand, AdbRequestStatus},
|
||||
models::{AdbCommand, AdbRequestStatus, AdbVersion},
|
||||
AdbCommandProvider, Device, DeviceState, Result, RustADBError,
|
||||
};
|
||||
|
||||
@@ -49,13 +50,22 @@ impl AdbTcpConnexion {
|
||||
let mut request_status = [0; 4];
|
||||
tcp_stream.read_exact(&mut request_status)?;
|
||||
|
||||
match AdbRequestStatus::from_str(String::from_utf8(request_status.to_vec())?.as_str())? {
|
||||
match AdbRequestStatus::from_str(str::from_utf8(&request_status.to_vec())?)? {
|
||||
AdbRequestStatus::Okay => {
|
||||
// Read 4 bytes length
|
||||
// Read exact length
|
||||
let mut length = [0; 4];
|
||||
tcp_stream.read_exact(&mut length)?;
|
||||
|
||||
let mut body: Vec<u8> = Vec::new();
|
||||
tcp_stream.read_to_end(&mut body)?;
|
||||
let u32_length = u32::from_str_radix(str::from_utf8(&length)?, 16)?;
|
||||
|
||||
let mut body = vec![
|
||||
0;
|
||||
u32_length
|
||||
.try_into()
|
||||
.map_err(|_| RustADBError::ConvertionError)?
|
||||
];
|
||||
if u32_length > 0 {
|
||||
tcp_stream.read_exact(&mut body)?;
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
@@ -78,12 +88,13 @@ impl Default for AdbTcpConnexion {
|
||||
}
|
||||
|
||||
impl AdbCommandProvider for AdbTcpConnexion {
|
||||
fn version(&self) -> Result<()> {
|
||||
fn version(&self) -> Result<AdbVersion> {
|
||||
let version = self.proxy_connexion(AdbCommand::Version)?;
|
||||
|
||||
println!("Version: {:?}", version.to_ascii_uppercase());
|
||||
|
||||
Ok(())
|
||||
Ok(AdbVersion::new(
|
||||
u32::from_str_radix(str::from_utf8(&version[0..2])?, 16)?,
|
||||
u32::from_str_radix(str::from_utf8(&version[2..4])?, 16)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn devices(&self) -> Result<Vec<Device>> {
|
||||
@@ -96,14 +107,12 @@ impl AdbCommandProvider for AdbTcpConnexion {
|
||||
}
|
||||
|
||||
let mut iter = device.split(|x| x.eq(&b'\t'));
|
||||
let identifier = iter.next().unwrap();
|
||||
let state = iter.next().unwrap();
|
||||
let identifier = iter.next().ok_or(RustADBError::IteratorError)?;
|
||||
let state = iter.next().ok_or(RustADBError::IteratorError)?;
|
||||
|
||||
vec_devices.push(Device {
|
||||
identifier: String::from_utf8(identifier.to_ascii_lowercase()).unwrap(),
|
||||
state: DeviceState::from_str(
|
||||
&String::from_utf8(state.to_ascii_lowercase()).unwrap(),
|
||||
)?,
|
||||
identifier: String::from_utf8(identifier.to_vec())?,
|
||||
state: DeviceState::from_str(str::from_utf8(state)?)?,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,11 +127,66 @@ impl AdbCommandProvider for AdbTcpConnexion {
|
||||
// Identifier = [0]
|
||||
// Device state = [1]
|
||||
|
||||
println!(
|
||||
"Devices long: {:?}",
|
||||
std::str::from_utf8(&devices_long.to_ascii_lowercase())
|
||||
);
|
||||
println!("Devices long: {:?}", std::str::from_utf8(&devices_long));
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn kill(&self) -> Result<()> {
|
||||
self.proxy_connexion(AdbCommand::Kill).map(|_| ())
|
||||
}
|
||||
|
||||
// TODO: Change with Generator when feature stabilizes
|
||||
fn track_devices(&self, callback: fn(Device) -> Result<()>) -> Result<()> {
|
||||
let mut tcp_stream = TcpStream::connect(self.socket_addr)?;
|
||||
|
||||
let adb_command_string = AdbCommand::TrackDevices.to_string();
|
||||
|
||||
let adb_request = format!(
|
||||
"{}{}",
|
||||
format!("{:04x}", adb_command_string.len()),
|
||||
adb_command_string
|
||||
);
|
||||
|
||||
tcp_stream.write_all(adb_request.as_bytes())?;
|
||||
|
||||
let mut request_status = [0; 4];
|
||||
tcp_stream.read_exact(&mut request_status)?;
|
||||
|
||||
match AdbRequestStatus::from_str(str::from_utf8(&request_status.to_vec())?)? {
|
||||
AdbRequestStatus::Okay => {
|
||||
loop {
|
||||
// Reads first 4 bytes indicating payload length
|
||||
let mut length = [0; 4];
|
||||
tcp_stream.read_exact(&mut length)?;
|
||||
|
||||
let u32_length = u32::from_str_radix(str::from_utf8(&length)?, 16)?;
|
||||
|
||||
if u32_length > 0 {
|
||||
let mut body = vec![
|
||||
0;
|
||||
u32_length
|
||||
.try_into()
|
||||
.map_err(|_| RustADBError::ConvertionError)?
|
||||
];
|
||||
tcp_stream.read_exact(&mut body)?;
|
||||
|
||||
let mut iter = body.split(|x| x.eq(&b'\t'));
|
||||
let identifier = iter.next().ok_or(RustADBError::IteratorError)?;
|
||||
let state = iter.next().ok_or(RustADBError::IteratorError)?;
|
||||
|
||||
let device = Device {
|
||||
identifier: String::from_utf8(identifier.to_vec())?,
|
||||
state: DeviceState::from_str(
|
||||
str::from_utf8(state)?.trim_matches('\n'),
|
||||
)?,
|
||||
};
|
||||
|
||||
callback(device)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
AdbRequestStatus::Fail => Err(RustADBError::ADBRequestFailed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/error.rs
10
src/error.rs
@@ -13,7 +13,15 @@ pub enum RustADBError {
|
||||
#[error("Unknown device state {0}")]
|
||||
UnknownDeviceState(String),
|
||||
#[error(transparent)]
|
||||
Utf8Error(#[from] std::string::FromUtf8Error),
|
||||
Utf8StrError(#[from] std::str::Utf8Error),
|
||||
#[error(transparent)]
|
||||
Utf8StringError(#[from] std::string::FromUtf8Error),
|
||||
#[error(transparent)]
|
||||
AddrParseError(#[from] std::net::AddrParseError),
|
||||
#[error(transparent)]
|
||||
ParseIntError(#[from] std::num::ParseIntError),
|
||||
#[error("Nothing to iterate over")]
|
||||
IteratorError,
|
||||
#[error("Convertion error")]
|
||||
ConvertionError,
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
pub enum AdbCommand {
|
||||
Version,
|
||||
Kill,
|
||||
Devices,
|
||||
DevicesLong,
|
||||
TrackDevices,
|
||||
}
|
||||
|
||||
impl ToString for AdbCommand {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
AdbCommand::Version => "host:version".into(),
|
||||
AdbCommand::Kill => "host:kill".into(),
|
||||
AdbCommand::Devices => "host:devices".into(),
|
||||
AdbCommand::DevicesLong => "host:devices-l".into(),
|
||||
AdbCommand::TrackDevices => "host:track-devices".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
src/models/adb_version.rs
Normal file
23
src/models/adb_version.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub struct AdbVersion {
|
||||
major: u32,
|
||||
minor: u32,
|
||||
revision: u32,
|
||||
}
|
||||
|
||||
impl AdbVersion {
|
||||
pub fn new(minor: u32, revision: u32) -> Self {
|
||||
Self {
|
||||
major: 1,
|
||||
minor,
|
||||
revision,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AdbVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}.{}.{}", self.major, self.minor, self.revision)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::DeviceState;
|
||||
|
||||
pub struct Device {
|
||||
pub identifier: String,
|
||||
pub state: DeviceState,
|
||||
}
|
||||
|
||||
impl Display for Device {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}\t{}", self.identifier, self.state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod adb_command;
|
||||
mod adb_request_status;
|
||||
mod adb_version;
|
||||
mod device;
|
||||
mod device_state;
|
||||
|
||||
pub use adb_command::AdbCommand;
|
||||
pub use adb_request_status::AdbRequestStatus;
|
||||
pub use adb_version::AdbVersion;
|
||||
pub use device::Device;
|
||||
pub use device_state::DeviceState;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use crate::{Device, Result};
|
||||
use crate::{models::AdbVersion, Device, Result};
|
||||
|
||||
/// Represents the property to serve ADB commands.
|
||||
pub trait AdbCommandProvider {
|
||||
/// Gets server's internal version number.
|
||||
fn version(&self) -> Result<()>;
|
||||
fn version(&self) -> Result<AdbVersion>;
|
||||
/// Gets a list of connected devices.
|
||||
fn devices(&self) -> Result<Vec<Device>>;
|
||||
/// Gets an extended list of connected devices including the device paths in the state.
|
||||
fn devices_long(&self) -> Result<Vec<Device>>;
|
||||
/// Asks the ADB server to quit immediately.
|
||||
fn kill(&self) -> Result<()>;
|
||||
/// Tracks new devices showing up.
|
||||
fn track_devices(&self, callback: fn(Device) -> Result<()>) -> Result<()>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user