@@ -26,6 +26,7 @@ const ANDROID_CMDLINE_TOOLS_URL: &str =
2626const ANDROID_CMDLINE_TOOLS_ARCHIVE : & str = "commandlinetools-mac-14742923_latest.zip" ;
2727const MANAGED_JAVA_FEATURE_VERSION : u16 = 21 ;
2828const SDKMANAGER_LICENSE_INPUT_REPEATS : usize = 200 ;
29+ const SDKMANAGER_PACKAGE_INSTALL_RETRIES : usize = 1 ;
2930
3031type 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缺失/不可执行错误。
13351383fn 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中的命令并收集输出。
13451476async fn run_command ( program : & str , args : & [ & str ] ) -> CommandProbe {
13461477 finish_probe ( {
0 commit comments