Skip to content

Commit 614c977

Browse files
WayneWayne
authored andcommitted
feat: 完善Android报错
1 parent 0616480 commit 614c977

1 file changed

Lines changed: 138 additions & 7 deletions

File tree

crates/simdock-core/src/provider/android.rs

Lines changed: 138 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const ANDROID_CMDLINE_TOOLS_URL: &str =
2626
const ANDROID_CMDLINE_TOOLS_ARCHIVE: &str = "commandlinetools-mac-14742923_latest.zip";
2727
const MANAGED_JAVA_FEATURE_VERSION: u16 = 21;
2828
const SDKMANAGER_LICENSE_INPUT_REPEATS: usize = 200;
29+
const SDKMANAGER_PACKAGE_INSTALL_RETRIES: usize = 1;
2930

3031
type ToolEnv = (&'static str, OsString);
3132

@@ -1132,7 +1133,7 @@ async fn install_android_packages(
11321133
let mut args = vec![format!("--sdk_root={}", sdk_root.display())];
11331134
args.extend(packages.iter().cloned());
11341135
let envs = android_tool_envs(sdk_root, avd_root, java_runtime);
1135-
let probe = run_path_command_streamed_with_input(
1136+
let mut probe = run_path_command_streamed_with_input(
11361137
sdkmanager,
11371138
&args,
11381139
&envs,
@@ -1143,13 +1144,48 @@ async fn install_android_packages(
11431144
.await;
11441145

11451146
if probe.success {
1146-
Ok(())
1147-
} else {
1148-
bail!(
1149-
"sdkmanager package installation failed: {}",
1150-
android_tool_failure_summary("sdkmanager", &probe, Some(java_runtime))
1151-
)
1147+
return Ok(());
11521148
}
1149+
1150+
if is_android_sdk_zip_error(&probe) {
1151+
for attempt in 1..=SDKMANAGER_PACKAGE_INSTALL_RETRIES {
1152+
emit_log(
1153+
sender,
1154+
task_id,
1155+
"Detected corrupted Android SDK package download; clearing sdkmanager cache before retry",
1156+
);
1157+
cleanup_android_sdk_download_state(sdk_root, packages, sender, task_id)?;
1158+
emit_log(
1159+
sender,
1160+
task_id,
1161+
format!(
1162+
"Retrying Android SDK package installation ({attempt}/{SDKMANAGER_PACKAGE_INSTALL_RETRIES})"
1163+
),
1164+
);
1165+
probe = run_path_command_streamed_with_input(
1166+
sdkmanager,
1167+
&args,
1168+
&envs,
1169+
Some(sdkmanager_license_input()),
1170+
task_id,
1171+
sender,
1172+
)
1173+
.await;
1174+
1175+
if probe.success {
1176+
return Ok(());
1177+
}
1178+
1179+
if !is_android_sdk_zip_error(&probe) {
1180+
break;
1181+
}
1182+
}
1183+
}
1184+
1185+
bail!(
1186+
"sdkmanager package installation failed: {}",
1187+
android_sdk_package_failure_summary(&probe, java_runtime)
1188+
)
11531189
}
11541190

11551191
/// 创建或复用Simdock管理的Android虚拟设备。
@@ -1331,6 +1367,18 @@ fn android_tool_failure_summary(
13311367
)
13321368
}
13331369

1370+
/// 为SDK包下载损坏提供更明确的失败原因和处理方向。
1371+
fn android_sdk_package_failure_summary(probe: &CommandProbe, java_runtime: &JavaRuntime) -> String {
1372+
if is_android_sdk_zip_error(probe) {
1373+
return format!(
1374+
"Android SDK package download appears corrupted even after retry. This is usually caused by an interrupted download or a company network proxy rewriting the zip file. Try another network, or ask IT to allow Android SDK downloads from dl.google.com/android/repository. Original output: {}",
1375+
probe.summary()
1376+
);
1377+
}
1378+
1379+
android_tool_failure_summary("sdkmanager", probe, Some(java_runtime))
1380+
}
1381+
13341382
/// 识别macOS和Android命令行工具输出中的Java缺失/不可执行错误。
13351383
fn is_android_java_runtime_error(probe: &CommandProbe) -> bool {
13361384
let output = format!("{}\n{}", probe.stdout, probe.stderr).to_lowercase();
@@ -1341,6 +1389,89 @@ fn is_android_java_runtime_error(probe: &CommandProbe) -> bool {
13411389
|| output.contains("java_home is not defined correctly")
13421390
}
13431391

1392+
/// 识别sdkmanager下载到损坏zip或准备SDK包失败的场景。
1393+
fn is_android_sdk_zip_error(probe: &CommandProbe) -> bool {
1394+
let output = format!("{}\n{}", probe.stdout, probe.stderr).to_lowercase();
1395+
output.contains("error reading zip content from a seekablebytechannel")
1396+
|| output.contains("zip end header not found")
1397+
|| output.contains("not a zip archive")
1398+
|| output.contains("error occurred while preparing sdk package") && output.contains("zip")
1399+
}
1400+
1401+
/// 清理sdkmanager的临时下载和不完整包目录,供损坏zip自动重试使用。
1402+
fn cleanup_android_sdk_download_state(
1403+
sdk_root: &Path,
1404+
packages: &[String],
1405+
sender: Option<&TaskSender>,
1406+
task_id: &str,
1407+
) -> Result<()> {
1408+
let temp_dir = sdk_root.join(".temp");
1409+
if remove_path_if_exists(&temp_dir)? {
1410+
emit_log(
1411+
sender,
1412+
task_id,
1413+
format!(
1414+
"Removed Android SDK temporary download directory: {}",
1415+
temp_dir.display()
1416+
),
1417+
);
1418+
}
1419+
1420+
for package in packages {
1421+
let Some(package_dir) = android_package_install_dir(sdk_root, package) else {
1422+
continue;
1423+
};
1424+
1425+
if package_dir.exists()
1426+
&& !package_dir.join("source.properties").exists()
1427+
&& remove_path_if_exists(&package_dir)?
1428+
{
1429+
emit_log(
1430+
sender,
1431+
task_id,
1432+
format!(
1433+
"Removed incomplete Android SDK package directory: {}",
1434+
package_dir.display()
1435+
),
1436+
);
1437+
}
1438+
}
1439+
1440+
Ok(())
1441+
}
1442+
1443+
/// 将sdkmanager包名映射成SDK目录,避免硬编码system image路径。
1444+
fn android_package_install_dir(sdk_root: &Path, package: &str) -> Option<PathBuf> {
1445+
let mut path = sdk_root.to_path_buf();
1446+
let mut has_segment = false;
1447+
1448+
for segment in package.split(';').map(str::trim) {
1449+
if segment.is_empty() || segment == "." || segment == ".." {
1450+
return None;
1451+
}
1452+
1453+
path.push(segment);
1454+
has_segment = true;
1455+
}
1456+
1457+
has_segment.then_some(path)
1458+
}
1459+
1460+
/// 删除文件或目录;不存在时视为成功,便于恢复流程幂等重试。
1461+
fn remove_path_if_exists(path: &Path) -> Result<bool> {
1462+
if !path.exists() {
1463+
return Ok(false);
1464+
}
1465+
1466+
if path.is_dir() {
1467+
fs::remove_dir_all(path)?;
1468+
} else {
1469+
fs::remove_file(path)?;
1470+
}
1471+
1472+
Ok(true)
1473+
}
1474+
13441475
/// 运行PATH中的命令并收集输出。
13451476
async fn run_command(program: &str, args: &[&str]) -> CommandProbe {
13461477
finish_probe({

0 commit comments

Comments
 (0)