Skip to content

Commit 1d61bf1

Browse files
committed
fix(adb): harden built-in USB backend
Propagate shell failures and only expose usable USB devices. Use the standard adb key path. Handle ADB server conflicts explicitly and improve connection errors.
1 parent f8e8c38 commit 1d61bf1

6 files changed

Lines changed: 457 additions & 71 deletions

File tree

crates/uad-cli/src/main.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use clap::{Parser, Subcommand, ValueEnum};
1212
use clap_complete::Shell;
1313
use std::process::ExitCode;
14-
use uad_core::adb::AdbBackend;
14+
use uad_core::adb::{ACommand, AdbBackend};
1515
use uad_core::uad_lists::PackageState;
1616

1717
mod commands;
@@ -58,6 +58,10 @@ pub struct Cli {
5858
)]
5959
backend: AdbBackendArg,
6060

61+
/// Stop the system ADB server before using the Builtin backend
62+
#[arg(long, global = true)]
63+
kill_adb_server: bool,
64+
6165
#[command(subcommand)]
6266
command: Commands,
6367
}
@@ -204,8 +208,7 @@ async fn main() -> ExitCode {
204208

205209
fn run() -> Result<(), Box<dyn std::error::Error>> {
206210
let cli = Cli::parse();
207-
let backend: AdbBackend = cli.backend.into();
208-
backend.set_current();
211+
configure_adb(&cli)?;
209212

210213
match cli.command {
211214
Commands::Devices => {
@@ -289,3 +292,75 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
289292

290293
Ok(())
291294
}
295+
296+
fn configure_adb(cli: &Cli) -> Result<(), Box<dyn std::error::Error>> {
297+
let backend: AdbBackend = cli.backend.into();
298+
#[cfg(feature = "builtin-adb")]
299+
let using_builtin = backend == AdbBackend::Builtin;
300+
#[cfg(not(feature = "builtin-adb"))]
301+
let using_builtin = false;
302+
303+
if cli.kill_adb_server && !using_builtin {
304+
return Err("--kill-adb-server requires --backend builtin".into());
305+
}
306+
if cli.kill_adb_server {
307+
ACommand::kill_system_server()
308+
.map_err(|err| format!("Failed to stop the system ADB server: {err}"))?;
309+
}
310+
311+
backend.set_current();
312+
313+
if using_builtin && !cli.kill_adb_server && command_uses_device(&cli.command) {
314+
let devices = ACommand::new().devices()?;
315+
if devices.iter().any(|(_, status)| status == "busy") {
316+
return Err(
317+
"The system ADB server is using the USB device. Stop it with `adb kill-server` \
318+
or rerun with `--kill-adb-server`."
319+
.into(),
320+
);
321+
}
322+
}
323+
324+
Ok(())
325+
}
326+
327+
fn command_uses_device(command: &Commands) -> bool {
328+
match command {
329+
Commands::Devices
330+
| Commands::List { .. }
331+
| Commands::Uninstall { .. }
332+
| Commands::Enable { .. }
333+
| Commands::Disable { .. }
334+
| Commands::Repl { .. } => true,
335+
Commands::Info { device, .. } => device.is_some(),
336+
Commands::Update | Commands::Adb | Commands::Completions { .. } => false,
337+
}
338+
}
339+
340+
#[cfg(test)]
341+
mod tests {
342+
use super::*;
343+
344+
#[cfg(feature = "builtin-adb")]
345+
#[test]
346+
fn builtin_server_shutdown_is_explicitly_opted_in() {
347+
let cli = Cli::try_parse_from([
348+
"uad",
349+
"--backend",
350+
"builtin",
351+
"--kill-adb-server",
352+
"devices",
353+
])
354+
.expect("valid Builtin CLI arguments");
355+
356+
assert_eq!(cli.backend, AdbBackendArg::Builtin);
357+
assert!(cli.kill_adb_server);
358+
assert!(command_uses_device(&cli.command));
359+
}
360+
361+
#[test]
362+
fn non_device_commands_skip_usb_preflight() {
363+
assert!(!command_uses_device(&Commands::Update));
364+
assert!(!command_uses_device(&Commands::Adb));
365+
}
366+
}

0 commit comments

Comments
 (0)