mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat(cli): restart Android emulator if it is disconnected from adb
needs https://github.com/tauri-apps/cargo-mobile2/pull/495 and https://github.com/tauri-apps/cargo-mobile2/pull/493
This commit is contained in:
6
.changes/restart-emulator.md
Normal file
6
.changes/restart-emulator.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@tauri-apps/cli": minor:feat
|
||||
"tauri-cli": minor:feat
|
||||
---
|
||||
|
||||
Prompt to restart the Android emulator if it is not connected to adb.
|
||||
57
Cargo.lock
generated
57
Cargo.lock
generated
@@ -234,6 +234,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-log",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-sample",
|
||||
"tiny_http",
|
||||
]
|
||||
@@ -1055,9 +1056,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cargo-mobile2"
|
||||
version = "0.21.1"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcea7efeaac9f0fd9f886f43a13dde186a1e2266fe6b53a42659e4e0689570de"
|
||||
checksum = "bab92dd12ad373b27af98de101f2ec7524634617c354c9ff34b880b3da8e5de3"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"core-foundation 0.10.0",
|
||||
@@ -2078,9 +2079,9 @@ checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055"
|
||||
|
||||
[[package]]
|
||||
name = "duct"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6ce170a0e8454fa0f9b0e5ca38a6ba17ed76a50916839d217eb5357e05cdfde"
|
||||
checksum = "d7478638a31d1f1f3d6c9f5e57c76b906a04ac4879d6fd0fb6245bc88f73fd0b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"os_pipe",
|
||||
@@ -4280,9 +4281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -5394,12 +5395,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7816,19 +7817,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shared_child"
|
||||
version = "1.0.1"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c"
|
||||
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"sigchld",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_thread"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7a6f98357c6bb0ebace19b22220e5543801d9de90ffe77f8abb27c056bac064"
|
||||
checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0"
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
@@ -7843,10 +7845,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
name = "sigchld"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"os_pipe",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -8840,6 +8853,16 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-process"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7461c622a5ea00eb9cd9f7a08dbd3bf79484499fd5c21aa2964677f64ca651ab"
|
||||
dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-sample"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -36,7 +36,7 @@ name = "cargo-tauri"
|
||||
path = "src/main.rs"
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
|
||||
cargo-mobile2 = { version = "0.21.1", default-features = false }
|
||||
cargo-mobile2 = { version = "0.22.1", default-features = false }
|
||||
|
||||
[dependencies]
|
||||
jsonrpsee = { version = "0.24", features = ["server"] }
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
use clap::{ArgAction, Parser};
|
||||
|
||||
use cargo_mobile2::{
|
||||
android::{adb, target::Target},
|
||||
android::{adb, device::ConnectionStatus, target::Target},
|
||||
opts::Profile,
|
||||
target::{call_for_targets_with_fallback, TargetTrait},
|
||||
};
|
||||
@@ -214,7 +214,11 @@ fn adb_forward_port(
|
||||
let forward = format!("tcp:{port}");
|
||||
log::info!("Forwarding port {port} with adb");
|
||||
|
||||
let mut devices = adb::device_list(env).unwrap_or_default();
|
||||
let mut devices = adb::device_list(env)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|d| d.status() == ConnectionStatus::Connected)
|
||||
.collect::<Vec<_>>();
|
||||
// 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!(
|
||||
@@ -226,7 +230,11 @@ fn adb_forward_port(
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
|
||||
devices = adb::device_list(env).unwrap_or_default();
|
||||
devices = adb::device_list(env)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|d| d.status() == ConnectionStatus::Connected)
|
||||
.collect::<Vec<_>>();
|
||||
if !devices.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use cargo_mobile2::{
|
||||
android::{
|
||||
adb,
|
||||
config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig},
|
||||
device::Device,
|
||||
device::{ConnectionStatus, Device},
|
||||
emulator,
|
||||
env::Env,
|
||||
target::Target,
|
||||
@@ -444,7 +444,11 @@ fn delete_codegen_vars() {
|
||||
}
|
||||
|
||||
fn adb_device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result<Device<'a>> {
|
||||
let device_list = adb::device_list(env).context("failed to detect connected Android devices")?;
|
||||
let device_list = adb::device_list(env)
|
||||
.context("failed to detect connected Android devices")?
|
||||
.into_iter()
|
||||
.filter(|d| d.status() == ConnectionStatus::Connected)
|
||||
.collect::<Vec<_>>();
|
||||
if !device_list.is_empty() {
|
||||
let device = if let Some(t) = target {
|
||||
let (device, score) = device_list
|
||||
@@ -530,31 +534,174 @@ fn emulator_prompt(env: &'_ Env, target: Option<&str>) -> Result<emulator::Emula
|
||||
}
|
||||
}
|
||||
|
||||
enum EmulatorStatus {
|
||||
Offline { serial_no: String },
|
||||
Connected,
|
||||
}
|
||||
|
||||
fn device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result<Device<'a>> {
|
||||
if let Ok(device) = adb_device_prompt(env, target) {
|
||||
Ok(device)
|
||||
} else {
|
||||
let emulator = emulator_prompt(env, target)?;
|
||||
log::info!("Starting emulator {}", emulator.name());
|
||||
emulator
|
||||
.start_detached(env)
|
||||
.context("failed to start emulator")?;
|
||||
let emulator_status = match adb::device_list(env) {
|
||||
Ok(devices) => {
|
||||
// emulator might be running but disconnected from adb
|
||||
devices
|
||||
.iter()
|
||||
.find(|d| d.name() == emulator.name())
|
||||
.and_then(|d| match d.status() {
|
||||
ConnectionStatus::Offline | ConnectionStatus::Unauthorized => {
|
||||
Some(EmulatorStatus::Offline {
|
||||
serial_no: d.serial_no().to_string(),
|
||||
})
|
||||
}
|
||||
ConnectionStatus::Connected => Some(EmulatorStatus::Connected),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
// failed to get device information, check if the device name matches the emulator name
|
||||
Err(
|
||||
adb::device_list::Error::ModelFailed {
|
||||
serial_no,
|
||||
error: adb::get_prop::Error::CommandFailed { command: _, error },
|
||||
}
|
||||
| adb::device_list::Error::AbiFailed {
|
||||
serial_no,
|
||||
error: adb::get_prop::Error::CommandFailed { command: _, error },
|
||||
},
|
||||
) => {
|
||||
if error.kind() == std::io::ErrorKind::TimedOut {
|
||||
// if the device name matches the emulator name, the emulator is already running and marked as connected
|
||||
// but we cannot connect to it
|
||||
adb::device_name(env, &serial_no).map_or(None, |device_name| {
|
||||
if device_name == emulator.name() {
|
||||
Some(EmulatorStatus::Offline { serial_no })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let emulator_already_running = emulator_status.is_some();
|
||||
match emulator_status {
|
||||
Some(EmulatorStatus::Offline { serial_no }) => {
|
||||
// emulator is available but not connected to adb, we must restart it
|
||||
log::info!("Emulator is not connected, we need to restart it");
|
||||
restart_emulator(&env, &serial_no, &emulator)?;
|
||||
}
|
||||
Some(EmulatorStatus::Connected) => {
|
||||
// emulator is already connected to adb
|
||||
// this is technically unreachable because we queried the device list with adb_device_prompt
|
||||
}
|
||||
None => {
|
||||
log::info!("Starting emulator {}", emulator.name());
|
||||
emulator
|
||||
.start_detached(env)
|
||||
.context("failed to start emulator")?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tries = 0;
|
||||
loop {
|
||||
sleep(Duration::from_secs(2));
|
||||
if let Ok(device) = adb_device_prompt(env, Some(emulator.name())) {
|
||||
return Ok(device);
|
||||
// we do not filter for connected devices to detect emulators that are not connected to our adb anymore
|
||||
match adb::device_list(env) {
|
||||
Ok(devices) => {
|
||||
if let Some(device) = devices.into_iter().find(|d| d.name() == emulator.name()) {
|
||||
if device.status() == ConnectionStatus::Connected {
|
||||
return Ok(device);
|
||||
}
|
||||
}
|
||||
|
||||
if tries >= 3 {
|
||||
log::info!("Waiting for emulator to start... (maybe the emulator is unauthorized or offline, run `adb devices` to check)");
|
||||
} else {
|
||||
log::info!("Waiting for emulator to start...");
|
||||
}
|
||||
tries += 1;
|
||||
}
|
||||
Err(
|
||||
adb::device_list::Error::ModelFailed {
|
||||
serial_no,
|
||||
error: adb::get_prop::Error::CommandFailed { command: _, error },
|
||||
}
|
||||
| adb::device_list::Error::AbiFailed {
|
||||
serial_no,
|
||||
error: adb::get_prop::Error::CommandFailed { command: _, error },
|
||||
},
|
||||
) => {
|
||||
if emulator_already_running && error.kind() == std::io::ErrorKind::TimedOut {
|
||||
log::info!("Emulator is not responding, we need to restart it");
|
||||
restart_emulator(&env, &serial_no, &emulator)?;
|
||||
tries = 0;
|
||||
} else {
|
||||
log::error!("failed to get properties for device {serial_no}: {error}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("failed to list devices with adb: {e}");
|
||||
tries += 1;
|
||||
}
|
||||
}
|
||||
if tries >= 3 {
|
||||
log::info!("Waiting for emulator to start... (maybe the emulator is unauthorized or offline, run `adb devices` to check)");
|
||||
} else {
|
||||
log::info!("Waiting for emulator to start...");
|
||||
}
|
||||
tries += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn restart_emulator(env: &Env, serial_no: &str, emulator: &emulator::Emulator) -> Result<()> {
|
||||
let granted_permission_to_restart =
|
||||
crate::helpers::prompts::confirm("Do you want to restart the emulator?", Some(true))
|
||||
.unwrap_or_default();
|
||||
if !granted_permission_to_restart {
|
||||
crate::error::bail!(
|
||||
"Cannot connect to the emulator, please restart it manually (a full boot might be required)"
|
||||
);
|
||||
}
|
||||
|
||||
adb::adb(env, &["-s", &serial_no, "emu", "kill"])
|
||||
.run()
|
||||
.context("failed to reboot emulator")?;
|
||||
|
||||
log::info!("Waiting for emulator to exit...");
|
||||
loop {
|
||||
let devices = adb::device_list(env).unwrap_or_default();
|
||||
if devices
|
||||
.into_iter()
|
||||
.find(|d| d.serial_no() == serial_no)
|
||||
.is_none()
|
||||
{
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_secs(1));
|
||||
}
|
||||
|
||||
log::info!("Restarting emulator with full boot");
|
||||
let mut tries = 0;
|
||||
loop {
|
||||
// wait a bit to make sure we can restart the emulator
|
||||
sleep(Duration::from_secs(2));
|
||||
|
||||
match emulator.start_detached_with_options(env, emulator::StartOptions::new().full_boot()) {
|
||||
Ok(_) => break,
|
||||
Err(e) => {
|
||||
tries += 1;
|
||||
if tries >= 3 {
|
||||
return Err(e).context("failed to start emulator");
|
||||
} else {
|
||||
log::error!("failed to start emulator, retrying...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> {
|
||||
device_prompt(env, None).map(|device| device.target()).ok()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user