Repository navigation
Description
Context
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulator(name, coldBoot) returns a bare System.Diagnostics.Process. Callers (e.g. dotnet/maui-labs maui android emulator start — see dotnet/maui-labs#138) want to surface to users / scripts:
- The emulator serial (
emulator-NNNN). - The console port and adb port.
- The log file path the emulator writes to.
Today none of this is available from EmulatorRunner without out-of-band work:
- Serial / ports → caller has to diff
adb devicesbefore/after launch (race-prone, requires waiting for adb to see it, breaks fire-and-forget mode). - Log path → caller has to hardcode the AOSP convention (
\/<name>.avd/emulator.logor\C:\Users\rumar/.android/avd/<name>.avd/emulator.log) — fine until the emulator is launched with-logfileor a customANDROID_AVD_HOME.
This duplicates logic in every consumer and is fragile. The data is all available to EmulatorRunner itself — the emulator prints serial/port info to stdout at boot, and EmulatorRunner controls the launch arg list.
Proposal
1. Richer launch result
Introduce a new return type and overload (keep existing LaunchEmulator signature for compatibility):
public sealed class EmulatorLaunchResult
{
public Process Process { get; init; }
public int Pid => Process.Id;
public int? ConsolePort { get; init; } // populated once stdout is parsed (or pre-assigned)
public int? AdbPort { get; init; }
public string? Serial => ConsolePort is int p ? \$"emulator-{p}" : null;
public string LogPath { get; init; } // resolved at launch time
public Task PortsResolvedAsync { get; init; } // completes when stdout parse finds the port lines
}
public EmulatorLaunchResult LaunchEmulatorWithDetails(
string avdName,
bool coldBoot = false,
int? consolePort = null, // optional pre-assignment via -ports
int? adbPort = null,
string? logFile = null);2. Pre-assigned ports (optional but recommended)
When consolePort is passed, EmulatorRunner adds -ports <console>,<adb> to the qemu command line. This lets callers know the serial before the process even starts — no stdout race, no adb diff. AOSP supports console ports 5554–5682 in steps of 2.
If not passed, EmulatorRunner reserves a free pair itself (probe + bind) or falls back to stdout parsing.
3. Stdout capture for late binding
When ports are not pre-assigned, EmulatorRunner should redirect stdout (it likely already does for diagnostics) and parse the well-known lines the emulator emits at boot:
emulator: Listening on port <console> emulator: ADB Server has started successfully on port <adb> emulator: Logs in <path>
These complete the PortsResolvedAsync task. Format has been stable across emulator releases for years; if it shifts, only this one place changes.
4. BootEmulatorAsync returns the same shape
BootEmulatorAsync (the wait-for-boot path) should return EmulatorLaunchResult enriched with Serial always populated (since by definition adb has seen it), instead of the current opaque result.
Compatibility
- Existing
LaunchEmulator/BootEmulatorAsyncsignatures stay — additive only. - New entry points are
LaunchEmulatorWithDetailsand aBootEmulatorWithDetailsAsyncoverload (or extend the existing result type with new optional members). EmulatorLaunchResultis a new public type.
Why this matters
- Eliminates the race in every downstream tool that pairs
maui device listagainstmaui android emulator start. - Makes
maui android emulator start --jsona complete, scriptable response (matches the new Googleandroid emulator startCLI shape: log path + pid + serial + status). - Enables non-
--waitlaunches to still return a serial — currently impossible without polling. - Removes the need for downstream tools to scavenge
\/running/pid_*.inito figure out which qemu PID maps to which emulator (seedotnet/maui-labs#135).
Related
dotnet/maui-labs#138— consumer-side enhancement that benefits directly.dotnet/maui-labs#135— pid-file based liveness (avoidable for emulators we launched ourselves once this lands).
Activity