Skip to content

Commit 103e35d

Browse files
CopilotPureWeenjsuarezruiz
authored andcommitted
Add emulator process kill timeout with ADB restart and comprehensive timeout protection (dotnet#30941)
* Initial plan * Implement emulator process kill timeout with ADB restart functionality Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Add timeout protection and error handling to PrepareDevice function - Added AdbCommandTimeoutSeconds constant (30 seconds) following existing timeout patterns - Created SafeAdbShell helper function with timeout protection for ADB shell commands - Created SafeAdbLogcat helper function with timeout protection for logcat operations - Updated PrepareDevice function to use safe ADB operations with proper error handling - Added try-catch blocks around EnsureAdbKeys, logcat setup, and property configuration - Operations continue gracefully on non-critical failures (logcat, properties) instead of throwing - Maintains existing functionality while preventing indefinite hangs in CI/CD environments Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Add ADB server restart at 90 seconds during emulator boot wait to recover from authorization issues Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Fix emulator process kill timeout mechanism to use WaitForExit instead of Task.Run Co-authored-by: jsuarezruiz <6755973+jsuarezruiz@users.noreply.github.com> * Fix AndroidEmulatorProcess WaitForExit compilation error by using Task.Run on WaitForExit instead of Kill Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Remove timeout protection from PrepareDevice function as requested Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Revert removal of timeout protection from PrepareDevice function Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> * Remove non-android.cake formatting changes and wrap emulator Kill() operation with timeout Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> Co-authored-by: jsuarezruiz <6755973+jsuarezruiz@users.noreply.github.com>
1 parent e809ce0 commit 103e35d

File tree

1 file changed

+144
-15
lines changed

1 file changed

+144
-15
lines changed

eng/devices/android.cake

Lines changed: 144 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const int DefaultApiLevel = 30;
66

77
const int EmulatorStartProcessTimeoutSeconds = 1 * 60;
88
const int EmulatorBootTimeoutSeconds = 2 * 60;
9+
const int EmulatorKillTimeoutSeconds = 1 * 60;
10+
const int AdbCommandTimeoutSeconds = 30;
911

1012
Information("Local Dotnet: {0}", localDotnet);
1113

@@ -473,8 +475,51 @@ void CleanUpVirtualDevice(AndroidEmulatorProcess emulatorProcess, AndroidAvdMana
473475

474476
// kill the process if it has not already exited
475477
Information("emulatorProcess.Kill()");
476-
try { emulatorProcess.Kill(); }
477-
catch { }
478+
try
479+
{
480+
// Wrap Kill() operation with timeout to prevent indefinite hanging
481+
var killTask = System.Threading.Tasks.Task.Run(() => emulatorProcess.Kill());
482+
if (killTask.Wait(TimeSpan.FromSeconds(EmulatorKillTimeoutSeconds)))
483+
{
484+
Information("Emulator process kill signal sent successfully.");
485+
486+
// Now wait for the process to actually exit
487+
var waitTask = System.Threading.Tasks.Task.Run(() => emulatorProcess.WaitForExit());
488+
if (waitTask.Wait(TimeSpan.FromSeconds(EmulatorKillTimeoutSeconds)))
489+
{
490+
Information("Emulator process killed successfully.");
491+
}
492+
else
493+
{
494+
Warning("Emulator process did not exit within {0} seconds after kill signal.", EmulatorKillTimeoutSeconds);
495+
}
496+
}
497+
else
498+
{
499+
Warning("Emulator process kill operation timed out after {0} seconds. Attempting to restart ADB server...", EmulatorKillTimeoutSeconds);
500+
501+
try
502+
{
503+
Information("Stopping ADB server...");
504+
AdbKillServer(adbSettings);
505+
System.Threading.Thread.Sleep(2000);
506+
507+
Information("Starting ADB server...");
508+
AdbStartServer(adbSettings);
509+
System.Threading.Thread.Sleep(2000);
510+
511+
Information("ADB server restart completed successfully.");
512+
}
513+
catch (Exception adbEx)
514+
{
515+
Error("Failed to restart ADB server after emulator kill timeout: {0}", adbEx.Message);
516+
}
517+
}
518+
}
519+
catch (Exception ex)
520+
{
521+
Warning("Failed to kill emulator process: {0}", ex.Message);
522+
}
478523

479524
if (deviceCreate)
480525
{
@@ -617,6 +662,44 @@ void GetDevices(string version, string toolPath)
617662
DotNetTool("tool", settings);
618663
}
619664

665+
IEnumerable<string> SafeAdbShell(string command, AdbToolSettings settings, int timeoutSeconds = AdbCommandTimeoutSeconds)
666+
{
667+
try
668+
{
669+
var shellTask = System.Threading.Tasks.Task.Run(() => AdbShell(command, settings));
670+
if (shellTask.Wait(TimeSpan.FromSeconds(timeoutSeconds)))
671+
{
672+
return shellTask.Result;
673+
}
674+
else
675+
{
676+
Warning("ADB shell command '{0}' timed out after {1} seconds", command, timeoutSeconds);
677+
return new string[0]; // Return empty array on timeout
678+
}
679+
}
680+
catch (Exception ex)
681+
{
682+
Warning("ADB shell command '{0}' failed: {1}", command, ex.Message);
683+
return new string[0]; // Return empty array on error
684+
}
685+
}
686+
687+
void SafeAdbLogcat(AdbLogcatOptions options, int timeoutSeconds = AdbCommandTimeoutSeconds)
688+
{
689+
try
690+
{
691+
var logcatTask = System.Threading.Tasks.Task.Run(() => AdbLogcat(options));
692+
if (!logcatTask.Wait(TimeSpan.FromSeconds(timeoutSeconds)))
693+
{
694+
Warning("ADB logcat operation timed out after {0} seconds", timeoutSeconds);
695+
}
696+
}
697+
catch (Exception ex)
698+
{
699+
Warning("ADB logcat operation failed: {0}", ex.Message);
700+
}
701+
}
702+
620703
void PrepareDevice(bool waitForBoot)
621704
{
622705
var settings = new AdbToolSettings { SdkRoot = androidSdkRoot };
@@ -632,7 +715,7 @@ void PrepareDevice(bool waitForBoot)
632715
// Wait for the emulator to finish booting
633716
var waited = 0;
634717
var total = EmulatorBootTimeoutSeconds;
635-
while (AdbShell("getprop sys.boot_completed", settings).FirstOrDefault() != "1")
718+
while (SafeAdbShell("getprop sys.boot_completed", settings).FirstOrDefault() != "1")
636719
{
637720
System.Threading.Thread.Sleep(1000);
638721

@@ -642,10 +725,40 @@ void PrepareDevice(bool waitForBoot)
642725
throw new Exception("The emulator did not finish booting in time.");
643726
}
644727

645-
if (waited % 60 == 0 && IsCIBuild())
728+
// At 90 seconds, restart ADB server to recover from authorization issues
729+
if (waited == 90 && IsCIBuild())
730+
{
731+
Information("Emulator boot taking longer than expected (90/{0} seconds). Restarting ADB server...", total);
732+
try
733+
{
734+
Information("Stopping ADB server...");
735+
AdbKillServer(settings);
736+
System.Threading.Thread.Sleep(2000);
737+
738+
Information("Starting ADB server...");
739+
AdbStartServer(settings);
740+
System.Threading.Thread.Sleep(2000);
741+
742+
Information("ADB server restart completed. Continuing to wait for emulator boot...");
743+
}
744+
catch (Exception ex)
745+
{
746+
Warning("Failed to restart ADB server during boot wait: {0}", ex.Message);
747+
// Continue without throwing - this is a recovery attempt
748+
}
749+
}
750+
else if (waited % 60 == 0 && IsCIBuild())
646751
{
647752
// Ensure ADB keys are configured
648-
EnsureAdbKeys(settings);
753+
try
754+
{
755+
EnsureAdbKeys(settings);
756+
}
757+
catch (Exception ex)
758+
{
759+
Warning("Failed to ensure ADB keys during boot wait: {0}", ex.Message);
760+
// Continue without throwing - this is a recovery attempt
761+
}
649762
}
650763
}
651764

@@ -656,22 +769,38 @@ void PrepareDevice(bool waitForBoot)
656769
{
657770
Information("Setting Logcat properties...");
658771

659-
AdbLogcat(new AdbLogcatOptions() { Clear = true });
660-
661-
AdbShell("logcat -G 16M", settings);
662-
663-
Information("Finished setting Logcat properties.");
772+
try
773+
{
774+
SafeAdbLogcat(new AdbLogcatOptions() { Clear = true });
775+
776+
SafeAdbShell("logcat -G 16M", settings);
777+
778+
Information("Finished setting Logcat properties.");
779+
}
780+
catch (Exception ex)
781+
{
782+
Warning("Failed to set Logcat properties: {0}", ex.Message);
783+
// Continue without throwing - logcat setup is not critical for device function
784+
}
664785
}
665786

666787
Information("Setting the ADB properties...");
667788

668-
var lines = AdbShell("setprop debug.mono.log default,mono_log_level=debug,mono_log_mask=all", settings);
669-
Information("{0}", string.Join("\n", lines));
789+
try
790+
{
791+
var lines = SafeAdbShell("setprop debug.mono.log default,mono_log_level=debug,mono_log_mask=all", settings);
792+
Information("{0}", string.Join("\n", lines));
670793

671-
lines = AdbShell("getprop debug.mono.log", settings);
672-
Information("{0}", string.Join("\n", lines));
794+
lines = SafeAdbShell("getprop debug.mono.log", settings);
795+
Information("{0}", string.Join("\n", lines));
673796

674-
Information("Finished setting ADB properties.");
797+
Information("Finished setting ADB properties.");
798+
}
799+
catch (Exception ex)
800+
{
801+
Warning("Failed to set ADB properties: {0}", ex.Message);
802+
// Continue without throwing - property setup failure should not stop the process
803+
}
675804
}
676805

677806
void EnsureAdbKeys(AdbToolSettings settings)

0 commit comments

Comments
 (0)