cocool97 / adb_client Goto Github PK
View Code? Open in Web Editor NEWRust ADB (Android Debug Bridge) client library
Rust ADB (Android Debug Bridge) client library
I noticed in the implementation of shell_command
that the responses from the TCP stream are being read into a buffer and then printed to stdout
.
match self.tcp_stream.read(&mut buffer) {
Ok(size) => {
if size == 0 {
return Ok(());
} else {
print!("{}", String::from_utf8(buffer.to_vec())?);
std::io::stdout().flush()?;
}
}
Err(e) => {
return Err(RustADBError::IOError(e));
}
}
I'm trying to capture the output when I execute a command via shell_command
but since the implementation returns an empty Ok(())
Result, there is no handle to stdout nor a handle to the buffer. What is the recommended way to capture this output for later use?
Currently the callback can only take fn pointers. I have an use case to pass the info onto a mpsc channel.
Let me know if there's another way to do it.
IIRC it should require a small change from
pub fn track_devices(&mut self, callback: fn(Device) -> Result<()>) -> Result<()> {
to
pub fn track_devices(&mut self, callback: impl Fn(Device) -> Result<()>) -> Result<()> {
Currently we have to do cargo install adb_client --example adb_cli
to get the CLI installed.
Is there another way to make it cleaner with only cargo install adb_client
?
pub fn shell_command<S: ToString>(
&mut self,
serial: &Option<S>,
command: impl IntoIterator<Item = S>,
) -> Result<Vec<u8>>
let connexion = AdbTcpConnexion::new();
let callback = |device:adb_client::Device| {
println!("{}", device);
Ok(())
};
println!("Live list of devices attached");
connexion.track_devices(callback)?;
system Env:
System: Macos 12.3.1
rust version: 1.59.0
Android Debug Bridge version 1.0.41
impl TryFrom<Vec<u8>> for Device {
type Error = RustADBError;
// TODO: Prevent regex compilation every call to try_from()
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
// Optional final '\n' is used to match TrackDevices inputs
let parse_regex = Regex::new("^(\\w+)\t(\\w+)\n?$")?;
let groups = match parse_regex.captures(&value).unwrap() // <----------- device.rs:29:51
Ok(Device {
identifier: String::from_utf8(
groups
.get(1)
.ok_or(RustADBError::RegexParsingError)?
.as_bytes()
.to_vec(),
)?,
state: DeviceState::from_str(&String::from_utf8(
groups
.get(2)
.ok_or(RustADBError::RegexParsingError)?
.as_bytes()
.to_vec(),
)?)?,
})
}
}
error output:
Live list of devices attached
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /Users/x/.cargo/registry/src/rsproxy.cn-8f6827c7555bfaf8/adb_client-0.2.0/src/models/device.rs:29:51
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
This command should take an apk path as a parameter, and install it on the targeted device.
If I call more than once adb's commands with the same AdbTcpConnection, I got a error message.
Error: IOError(Error { kind: UnexpectedEof, message: "failed to fill whole buffer" })
It's the fact of "reusing" the same object to run adb commands which used tcp steam to retrieved the result from adb server. Maybe the buffer used by tcp stream is not cleared correctly, but I'm not good enough with Rust to know how it's handled. Anyway, I think it's must be possible to not create a new connection each times that you called a method.
Here a simple example that call 2 times in a row the adb command "version" which produce error (I used command "version" for simplicity's sake but it's the same resul, regardless of the method used)
[package]
name = "adb_example"
version = "0.1.0"
edition = "2021"
[dependencies]
adb_client = { version = "1.0.1" }
use std::net::Ipv4Addr;
use adb_client::{AdbTcpConnection, RustADBError};
fn main() -> Result<(), RustADBError> {
let mut connection = AdbTcpConnection::new(Ipv4Addr::from([127, 0, 0, 1]), 5037)?;
for _ in 0..2 {
let version = connection.version()?;
println!("Android Debug Bridge version {}", version);
}
Ok(())
}
A way making things work with the same code that throw a error is for example to set up a new connection after using the current one. I added one line in your adb "version" implementation but I think it gets round the problem without solving it. But that's perhaps to be expected given the way tcpstream works, I don't know at all.
impl AdbTcpConnection {
/// Gets server's internal version number.
pub fn version(&mut self) -> Result<AdbVersion> {
let version = self.proxy_connection(AdbCommand::Version, true)?;
self.new_connection(); # Added
AdbVersion::try_from(version)
}
}
}
This command should run an already installed apk. Parameter could be the activity name to launch.
Is it possible to implement the ADB pairing process? For Android version >= 11, pairing is needed before connection.
IRust shell
In: :add adb_client
Adding dep [-]
Updating crates.io index
Adding adb_client v1.0.0 to dependencies.
Updating crates.io index
In: use std::net::Ipv4Addr;
In: use adb_client::*;
In: let mut connection = AdbTcpConnection::new(Ipv4Addr::from([127,0,0,1]), 5037).unwrap();
In: connection.devices()
Out: thread 'main' panicked at /home/vladimir/.local/share/rtx/installs/rust/1.77.1/registry/src/index.crates.io-6f17d22bba15001f/adb_client-1.0.0/src/models/device.rs:30:51:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
In: connection.devices()
Out: Ok([])
Works well with no devices, but crashes with emulator connected
$ adb devices
List of devices attached
emulator-5554 device
I suggest to replace Regex::new("^(\\w+)\t(\\w+)\n?$")
with Regex::new("^(\\S+)\t(\\w+)\n?$")
and don't use unwrap()
.
P.S.
You can probably use lazy_static
to prevent regex compilation every time
cargo.exe build
error[E0433]: failed to resolve: could not find unix
in os
--> C:\Users\12289.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\termios-0.3.3\src\lib.rs:132:14
|
132 | use std::os::unix::io::RawFd;
| ^^^^ could not find unix
in os
error[E0432]: unresolved import libc::pid_t
--> C:\Users\12289.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\termios-0.3.3\src\lib.rs:134:18
|
134 | use libc::{c_int,pid_t};
| ^^^^^ no pid_t
in the root
error[E0432]: unresolved import os::target
--> C:\Users\12289.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\termios-0.3.3\src\lib.rs:136:15
|
136 | pub use ::os::target::{cc_t,speed_t,tcflag_t}; // types
| ^^^^^^ could not find target
in os
error[E0432]: unresolved import os::target
--> C:\Users\12289.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\termios-0.3.3\src\lib.rs:137:15
|
137 | pub use ::os::target::{VEOF,VEOL,VERASE,VINTR,VKILL,VMIN,VQUIT,VSTART,VSTOP,VSUSP,VTIME}; // c_cc subscripts
| ^^^^^^ could not find target
in os
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.