-
Notifications
You must be signed in to change notification settings - Fork 73
Expand file tree
/
Copy pathmain.rs
More file actions
227 lines (200 loc) · 8.45 KB
/
main.rs
File metadata and controls
227 lines (200 loc) · 8.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#![doc = include_str!("../README.md")]
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod adb_termios;
mod handlers;
mod models;
mod utils;
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::{ADBDeviceInfo, ADBUSBDevice, find_all_connected_adb_devices};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use adb_termios::ADBTermios;
use clap::Parser;
use handlers::{handle_emulator_commands, handle_host_commands, handle_local_commands};
use models::{DeviceCommands, LocalCommand, MainCommand, Opts};
use std::collections::HashMap;
use std::fs::File;
use std::io::{Write, stdout};
use std::path::Path;
use std::process::ExitCode;
use tabwriter::TabWriter;
use utils::setup_logger;
use crate::models::{ADBCliError, ADBCliResult};
fn run_command(mut device: Box<dyn ADBDeviceExt>, command: DeviceCommands) -> ADBCliResult<()> {
match command {
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..
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let mut adb_termios = ADBTermios::new(&std::io::stdin())?;
adb_termios.set_adb_termios()?;
device.shell(&mut std::io::stdin(), Box::new(std::io::stdout()))?;
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
device.shell(&mut std::io::stdin(), Box::new(std::io::stdout()))?;
}
} else {
device.shell_command(&commands.join(" "), &mut std::io::stdout())?;
}
}
DeviceCommands::Pull {
source,
destination,
} => {
let mut output = File::create(Path::new(&destination))?;
device.pull(&source, &mut output)?;
log::info!("Downloaded {source} as {destination}");
}
DeviceCommands::Stat { path } => {
let stat_response = device.stat(&path)?;
println!("{stat_response}");
}
DeviceCommands::Reboot { reboot_type } => {
log::info!("Reboots device in mode {reboot_type:?}");
device.reboot(reboot_type.into())?;
}
DeviceCommands::Push { filename, path } => {
let mut input = File::open(Path::new(&filename))?;
device.push(&mut input, &path)?;
log::info!("Uploaded {filename} to {path}");
}
DeviceCommands::Root => {
device.root()?;
log::info!("Restarted adbd as root");
}
DeviceCommands::Run { package, activity } => {
let output = device.run_activity(&package, &activity)?;
std::io::stdout().write_all(&output)?;
}
DeviceCommands::Install { path, user } => {
log::info!("Starting installation of APK {}...", path.display());
device.install(&path, user.as_deref())?;
}
DeviceCommands::Uninstall { package, user } => {
log::info!("Uninstalling the package {package}...");
device.uninstall(&package, user.as_deref())?;
}
DeviceCommands::Framebuffer { path } => {
device.framebuffer(&path)?;
log::info!("Successfully dumped framebuffer at path {path}");
}
DeviceCommands::List { path } => {
let dirs = device.list(&path)?;
for dir in dirs {
log::info!("{dir}");
}
}
}
Ok(())
}
fn main() -> ExitCode {
if let Err(err) = inner_main() {
log::error!("{err}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}
fn inner_main() -> ADBCliResult<()> {
// This depends on `clap`
let opts = Opts::parse();
setup_logger(opts.debug);
// Directly handling methods / commands that aren't linked to [`ADBDeviceExt`] trait.
// Other methods just have to create a concrete [`ADBDeviceExt`] instance, and return it.
// This instance will then be used to execute desired command.
let (device, commands) = match opts.command {
MainCommand::Host(server_command) => return Ok(handle_host_commands(server_command)?),
MainCommand::Emu(emulator_command) => return handle_emulator_commands(emulator_command),
MainCommand::Local(server_command) => {
// Must start server to communicate with device, but only if this is a local one.
let server_address_ip = server_command.address.ip();
if server_address_ip.is_loopback() || server_address_ip.is_unspecified() {
ADBServer::start(&HashMap::default(), &None);
}
let device = match server_command.serial {
Some(serial) => ADBServerDevice::new(serial, Some(server_command.address)),
None => ADBServerDevice::autodetect(Some(server_command.address)),
};
match server_command.command {
LocalCommand::DeviceCommands(device_commands) => (device.boxed(), device_commands),
LocalCommand::LocalDeviceCommand(local_device_command) => {
return handle_local_commands(device, local_device_command);
}
}
}
MainCommand::Usb(usb_command) => {
if usb_command.list_devices {
let devices = find_all_connected_adb_devices()?;
let mut writer = TabWriter::new(stdout()).alignment(tabwriter::Alignment::Center);
writeln!(writer, "Index\tVendor ID\tProduct ID\tDevice Description")?;
writeln!(writer, "-----\t---------\t----------\t----------------")?;
for (
index,
ADBDeviceInfo {
vendor_id,
product_id,
device_description,
},
) in devices.iter().enumerate()
{
writeln!(
writer,
"#{index}\t{vendor_id:04x}\t{product_id:04x}\t{device_description}",
)?;
}
writer.flush()?;
return Ok(());
}
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)?,
},
(None, None) => match usb_command.path_to_private_key {
Some(pk) => ADBUSBDevice::autodetect_with_custom_private_key(pk)?,
None => ADBUSBDevice::autodetect()?,
},
_ => {
return Err(ADBCliError::Standard(
"cannot specify flags --vendor-id without --product-id or vice versa"
.into(),
));
}
};
if let Some(command) = usb_command.commands {
(device.boxed(), command)
} else {
return Err(ADBCliError::Standard("no command specified".into()));
}
}
MainCommand::Tcp(tcp_command) => {
let device = match tcp_command.path_to_private_key {
Some(pk) => ADBTcpDevice::new_with_custom_private_key(tcp_command.address, pk)?,
None => ADBTcpDevice::new(tcp_command.address)?,
};
(device.boxed(), tcp_command.commands)
}
MainCommand::Mdns => {
let mut service = MDNSDiscoveryService::new()?;
let (tx, rx) = std::sync::mpsc::channel();
service.start(tx)?;
log::info!("Starting mdns discovery...");
while let Ok(device) = rx.recv() {
log::info!(
"Found device fullname='{}' with ipv4 addresses={:?} and ipv6 addresses={:?}",
device.fullname,
device.ipv4_addresses(),
device.ipv6_addresses()
);
}
return Ok(service.shutdown()?);
}
};
run_command(device, commands)?;
Ok(())
}