Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Xamarin.Android.Tools.AndroidSdk/Runners/EmulatorRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,33 @@ public async Task<EmulatorBootResult> BootEmulatorAsync (
// Poll for the new emulator serial to appear.
// If the boot times out or is cancelled, terminate the process we spawned
// to avoid leaving orphan emulator processes.
//
// On macOS, the emulator binary may fork the real QEMU process and exit with
// code 0 immediately. The real emulator continues as a separate process and
// will eventually appear in 'adb devices'. We only treat non-zero exit codes
// as immediate failures; exit code 0 means we continue polling.
try {
string? newSerial = null;
bool processExitedWithZero = false;
while (newSerial == null) {
timeoutCts.Token.ThrowIfCancellationRequested ();

// Detect early process exit for fast failure
if (emulatorProcess.HasExited && !processExitedWithZero) {
if (emulatorProcess.ExitCode != 0) {
emulatorProcess.Dispose ();
return new EmulatorBootResult {
Success = false,
ErrorKind = EmulatorBootErrorKind.LaunchFailed,
ErrorMessage = $"Emulator process for '{deviceOrAvdName}' exited with code {emulatorProcess.ExitCode} before becoming available.",
};
}
// Exit code 0: emulator likely forked (common on macOS).
// The real emulator runs as a separate process — keep polling.
logger.Invoke (TraceLevel.Verbose, $"Emulator launcher process exited with code 0 (likely forked). Continuing to poll adb devices.");
processExitedWithZero = true;
}

await Task.Delay (options.PollInterval, timeoutCts.Token).ConfigureAwait (false);

devices = await adbRunner.ListDevicesAsync (timeoutCts.Token).ConfigureAwait (false);
Expand Down