Skip to content

Commit

Permalink
feat(cli): enhance Android dev port forwarding, closes #11137
Browse files Browse the repository at this point in the history
this changes the `android dev` port forwarding (that is actually handled by the `android-studio-script` command - triggered by our Gradle plugin) with some enhancements:

- make the whole process more resilient by checking if the port was actually forwarded and rerunning the `adb reverse` command until it tells us the forward is ready
- if the `adb devices` list is empty, retry a few times (waiting a few seconds) to tolerate devices being booted - slows down "raw builds" (Build Project Android Studio menu for instance) that shouldn't happen often anyway - if you're running `android dev` you're usually running the app on a device instead of simply testing builds
  • Loading branch information
lucasfernog committed Oct 1, 2024
1 parent 60a5aea commit 4c24e06
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 26 deletions.
6 changes: 6 additions & 0 deletions .changes/enhance-android-port-forwarding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'tauri-cli': 'patch:enhance'
'@tauri-apps/cli': 'patch:enhance'
---

Enhance port forwarding on `android dev` to be more resilient and tolerate delays when booting up devices.
111 changes: 85 additions & 26 deletions crates/tauri-cli/src/mobile/android/android_studio_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,32 +91,93 @@ pub fn command(options: Options) -> Result<()> {
let forward = format!("tcp:{port}");
log::info!("Forwarding port {port} with adb");

let devices = adb::device_list(&env).unwrap_or_default();
let mut devices = adb::device_list(&env).unwrap_or_default();
// if we could not detect any running device, let's wait a few seconds, it might be booting up
if devices.is_empty() {
log::warn!(
"ADB device list is empty, waiting a few seconds to see if there's any booting device..."
);

// clear port forwarding for all devices
for device in &devices {
remove_adb_reverse(&env, device.serial_no(), &forward);
let max = 5;
let mut count = 0;
loop {
std::thread::sleep(std::time::Duration::from_secs(1));

devices = adb::device_list(&env).unwrap_or_default();
if !devices.is_empty() {
break;
}

count += 1;
if count == max {
break;
}
}
}

// if there's a known target, we should force use it
if let Some(target_device) = &cli_options.target_device {
run_adb_reverse(&env, &target_device.id, &forward, &forward).with_context(|| {
format!(
"failed to forward port with adb, is the {} device connected?",
target_device.name,
)
})?;
let target_device = if let Some(target_device) = &cli_options.target_device {
Some((target_device.id.clone(), target_device.name.clone()))
} else if devices.len() == 1 {
let device = devices.first().unwrap();
run_adb_reverse(&env, device.serial_no(), &forward, &forward).with_context(|| {
format!(
"failed to forward port with adb, is the {} device connected?",
device.name(),
)
})?;
Some((device.serial_no().to_string(), device.name().to_string()))
} else if devices.len() > 1 {
anyhow::bail!("Multiple Android devices are connected ({}), please disconnect devices you do not intend to use so Tauri can determine which to use",
devices.iter().map(|d| d.name()).collect::<Vec<_>>().join(", "));
} else {
// when building the app without running to a device, we might have an empty devices list
None
};

if let Some((target_device_serial_no, target_device_name)) = target_device {
let mut already_forwarded = false;

// clear port forwarding for all devices
for device in &devices {
let reverse_list_output = adb_reverse_list(&env, device.serial_no())?;

// check if the device has the port forwarded
if String::from_utf8_lossy(&reverse_list_output.stdout).contains(&forward) {
// device matches our target, we can skip forwarding
if device.serial_no() == target_device_serial_no {
log::debug!(
"device {} already has the forward for {}",
device.name(),
forward
);
already_forwarded = true;
}
break;
}
}

// if there's a known target, we should forward the port to it
if already_forwarded {
log::info!("{forward} already forwarded to {target_device_name}");
} else {
loop {
run_adb_reverse(&env, &target_device_serial_no, &forward, &forward).with_context(
|| {
format!(
"failed to forward port with adb, is the {target_device_name} device connected?",
)
},
)?;

let reverse_list_output = adb_reverse_list(&env, &target_device_serial_no)?;
// wait and retry until the port has actually been forwarded
if String::from_utf8_lossy(&reverse_list_output.stdout).contains(&forward) {
break;
} else {
log::warn!(
"waiting for the port to be forwarded to {}...",
target_device_name
);
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
}
} else {
log::warn!("no running devices detected with ADB; skipping port forwarding");
}
}
}
Expand Down Expand Up @@ -193,15 +254,13 @@ fn run_adb_reverse(
.run()
}

fn remove_adb_reverse(
fn adb_reverse_list(
env: &cargo_mobile2::android::env::Env,
device_serial_no: &str,
remote: &str,
) {
// ignore errors in case the port is not forwarded
let _ = adb::adb(env, ["-s", device_serial_no, "reverse", "--remove", remote])
) -> std::io::Result<std::process::Output> {
adb::adb(env, ["-s", device_serial_no, "reverse", "--list"])
.stdin_file(os_pipe::dup_stdin().unwrap())
.stdout_file(os_pipe::dup_stdout().unwrap())
.stderr_file(os_pipe::dup_stdout().unwrap())
.run();
.stdout_capture()
.stderr_capture()
.run()
}

0 comments on commit 4c24e06

Please sign in to comment.