Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add framebuffer method to USB/TCP direct devices #69

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions adb_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ mod usb;
pub use emu::EmuCommand;
pub use host::{HostCommand, MdnsCommand};
pub use local::LocalCommand;
pub use tcp::{TcpCommand, TcpCommands};
pub use usb::{UsbCommand, UsbCommands};
pub use tcp::TcpCommand;
pub use usb::{DeviceCommands, UsbCommand};
39 changes: 3 additions & 36 deletions adb_cli/src/commands/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
use std::net::SocketAddr;
use std::path::PathBuf;

use clap::Parser;
use std::net::SocketAddr;

use crate::models::RebootTypeCommand;
use super::DeviceCommands;

#[derive(Parser, Debug)]
pub struct TcpCommand {
pub address: SocketAddr,
#[clap(subcommand)]
pub commands: TcpCommands,
}

#[derive(Parser, Debug)]
pub enum TcpCommands {
/// Spawn an interactive shell or run a list of commands on the device
Shell { commands: Vec<String> },
/// Pull a file from device
Pull { source: String, destination: String },
/// Push a file on device
Push { filename: String, path: String },
/// Stat a file on device
Stat { path: String },
/// Run an activity on device specified by the intent
Run {
/// The package whose activity is to be invoked
#[clap(short = 'p', long = "package")]
package: String,
/// The activity to be invoked itself, Usually it is MainActivity
#[clap(short = 'a', long = "activity")]
activity: String,
},
/// Reboot the device
Reboot {
#[clap(subcommand)]
reboot_type: RebootTypeCommand,
},
/// Install an APK on device
Install {
/// Path to APK file. Extension must be ".apk"
path: PathBuf,
},
pub commands: DeviceCommands,
}
9 changes: 7 additions & 2 deletions adb_cli/src/commands/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ pub struct UsbCommand {
#[clap(short = 'k', long = "private-key")]
pub path_to_private_key: Option<PathBuf>,
#[clap(subcommand)]
pub commands: UsbCommands,
pub commands: DeviceCommands,
}

#[derive(Parser, Debug)]
pub enum UsbCommands {
pub enum DeviceCommands {
/// Spawn an interactive shell or run a list of commands on the device
Shell { commands: Vec<String> },
/// Pull a file from device
Expand Down Expand Up @@ -53,4 +53,9 @@ pub enum UsbCommands {
/// Path to APK file. Extension must be ".apk"
path: PathBuf,
},
/// Dump framebuffer of device
Framebuffer {
/// Framebuffer image destination path
path: String,
},
}
32 changes: 17 additions & 15 deletions adb_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use adb_client::{
};
use anyhow::{anyhow, Result};
use clap::Parser;
use commands::{EmuCommand, HostCommand, LocalCommand, MdnsCommand, TcpCommands, UsbCommands};
use commands::{DeviceCommands, EmuCommand, HostCommand, LocalCommand, MdnsCommand};
use models::{Command, Opts};
use std::fs::File;
use std::io::Write;
Expand Down Expand Up @@ -226,7 +226,7 @@ fn main() -> Result<()> {
};

match usb.commands {
UsbCommands::Shell { commands } => {
DeviceCommands::Shell { commands } => {
if commands.is_empty() {
// Need to duplicate some code here as ADBTermios [Drop] implementation resets terminal state.
// Using a scope here would call drop() too early..
Expand All @@ -245,42 +245,43 @@ fn main() -> Result<()> {
device.shell_command(commands, std::io::stdout())?;
}
}
UsbCommands::Pull {
DeviceCommands::Pull {
source,
destination,
} => {
let mut output = File::create(Path::new(&destination))?;
device.pull(&source, &mut output)?;
log::info!("Downloaded {source} as {destination}");
}
UsbCommands::Stat { path } => {
DeviceCommands::Stat { path } => {
let stat_response = device.stat(&path)?;
println!("{}", stat_response);
}
UsbCommands::Reboot { reboot_type } => {
DeviceCommands::Reboot { reboot_type } => {
log::info!("Reboots device in mode {:?}", reboot_type);
device.reboot(reboot_type.into())?
}
UsbCommands::Push { filename, path } => {
DeviceCommands::Push { filename, path } => {
let mut input = File::open(Path::new(&filename))?;
device.push(&mut input, &path)?;
log::info!("Uploaded {filename} to {path}");
}
UsbCommands::Run { package, activity } => {
DeviceCommands::Run { package, activity } => {
let output = device.run_activity(&package, &activity)?;
std::io::stdout().write_all(&output)?;
}
UsbCommands::Install { path } => {
DeviceCommands::Install { path } => {
log::info!("Starting installation of APK {}...", path.display());
device.install(path)?;
}
DeviceCommands::Framebuffer { path } => device.framebuffer(path)?,
}
}
Command::Tcp(tcp) => {
let mut device = ADBTcpDevice::new(tcp.address)?;

match tcp.commands {
TcpCommands::Shell { commands } => {
DeviceCommands::Shell { commands } => {
if commands.is_empty() {
// Need to duplicate some code here as ADBTermios [Drop] implementation resets terminal state.
// Using a scope here would call drop() too early..
Expand All @@ -299,35 +300,36 @@ fn main() -> Result<()> {
device.shell_command(commands, std::io::stdout())?;
}
}
TcpCommands::Pull {
DeviceCommands::Pull {
source,
destination,
} => {
let mut output = File::create(Path::new(&destination))?;
device.pull(&source, &mut output)?;
log::info!("Downloaded {source} as {destination}");
}
TcpCommands::Stat { path } => {
DeviceCommands::Stat { path } => {
let stat_response = device.stat(&path)?;
println!("{}", stat_response);
}
TcpCommands::Reboot { reboot_type } => {
DeviceCommands::Reboot { reboot_type } => {
log::info!("Reboots device in mode {:?}", reboot_type);
device.reboot(reboot_type.into())?
}
TcpCommands::Push { filename, path } => {
DeviceCommands::Push { filename, path } => {
let mut input = File::open(Path::new(&filename))?;
device.push(&mut input, &path)?;
log::info!("Uploaded {filename} to {path}");
}
TcpCommands::Run { package, activity } => {
DeviceCommands::Run { package, activity } => {
let output = device.run_activity(&package, &activity)?;
std::io::stdout().write_all(&output)?;
}
TcpCommands::Install { path } => {
DeviceCommands::Install { path } => {
log::info!("Starting installation of APK {}...", path.display());
device.install(path)?;
}
DeviceCommands::Framebuffer { path } => device.framebuffer(path)?,
}
}
Command::MdnsDiscovery => {
Expand Down
16 changes: 15 additions & 1 deletion adb_client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ 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|<div style="color:green">-1,57 %</div>|
|500 MB|50|15,60 s|15,64 s|<div style="color:green">-0,25 %</div>|
|1 GB|20|31,09 s|31,12 s|<div style="color:green">-0,10 %</div>|

## Examples

### Get available ADB devices
Expand Down Expand Up @@ -84,7 +98,7 @@ let mut input = File::open(Path::new("/tmp/f")).expect("Cannot open file");
device.push(&mut input, "/data/local/tmp");
```

### (TCP) Get a shell from device
#### (TCP) Get a shell from device

```rust no_run
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
Expand Down
10 changes: 9 additions & 1 deletion adb_client/src/adb_device_ext.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::{Read, Write};
use std::io::{Read, Seek, Write};
use std::path::Path;

use crate::models::AdbStatResponse;
Expand Down Expand Up @@ -43,4 +43,12 @@ pub trait ADBDeviceExt {

/// Install an APK pointed to by `apk_path` on device.
fn install<P: AsRef<Path>>(&mut self, apk_path: P) -> Result<()>;

/// Dump framebuffer of this device into given path
fn framebuffer<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;

/// Dump framebuffer of this device and return corresponding bytes.
///
/// Output data format is currently only `PNG`.
fn framebuffer_bytes<W: Write + Seek>(&mut self, writer: W) -> Result<()>;
}
Loading
Loading