Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9e408d8
Add EmulatorRunner for emulator CLI operations
rmarinho Mar 9, 2026
d0d188a
Fix EmulatorPath Windows lookup and test fake to use .bat
rmarinho Mar 10, 2026
5fb7e6e
Address review findings: structured args, record types, pipe draining…
rmarinho Mar 11, 2026
7e42eff
Fix env vars, timeout duplication, and process tree cleanup
rmarinho Mar 12, 2026
97c05e0
Rename StartAvd→LaunchAvd and BootAndWaitAsync→BootAvdAsync
rmarinho Mar 13, 2026
b473917
Address review findings: validation, logging, tests
rmarinho Mar 13, 2026
21f8b94
Fix Process handle leak on successful BootAvdAsync
rmarinho Mar 13, 2026
f19b014
Address bot review: exit code check, typed catch, shell doc
rmarinho Mar 13, 2026
d8ee2d5
Rename LaunchAvd→LaunchEmulator, BootAvdAsync→BootEmulatorAsync, add …
rmarinho Mar 13, 2026
b462f24
Add tests ported from dotnet/android BootAndroidEmulator
rmarinho Mar 16, 2026
8cbb0ae
Fix AVD name detection for emulator 36+ with getprop fallback
rmarinho Mar 17, 2026
63c0fcf
Address PR review: record type, List<string>, simplify code
rmarinho Mar 17, 2026
d12dde7
Apply NullLogger pattern across all runners
rmarinho Mar 17, 2026
2bf8d67
Add EmulatorBootErrorKind enum to EmulatorBootResult
rmarinho Mar 18, 2026
e46f7de
Consolidate NullLogger and remove null-forgiving operator
rmarinho Mar 18, 2026
92d5e25
Fix inaccurate comment about emulator console auth
rmarinho Mar 18, 2026
c8927cc
Fix EmulatorRunner early exit detection and macOS fork handling
rmarinho Mar 18, 2026
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
18 changes: 18 additions & 0 deletions src/Xamarin.Android.Tools.AndroidSdk/Models/EmulatorBootOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Xamarin.Android.Tools;

/// <summary>
/// Options for booting an Android emulator.
/// </summary>
public record EmulatorBootOptions
{
public TimeSpan BootTimeout { get; init; } = TimeSpan.FromSeconds (300);
public List<string>? AdditionalArgs { get; init; }
public bool ColdBoot { get; init; }
public TimeSpan PollInterval { get; init; } = TimeSpan.FromMilliseconds (500);
}
41 changes: 41 additions & 0 deletions src/Xamarin.Android.Tools.AndroidSdk/Models/EmulatorBootResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Xamarin.Android.Tools;

/// <summary>
/// Classifies the reason an emulator boot operation failed.
/// </summary>
public enum EmulatorBootErrorKind
{
/// <summary>No error — the boot succeeded.</summary>
None,

/// <summary>The emulator process could not be launched (e.g., binary not found, AVD missing).</summary>
LaunchFailed,

/// <summary>The emulator launched but did not finish booting within the allowed timeout.</summary>
Timeout,

/// <summary>The boot was cancelled via <see cref="System.Threading.CancellationToken"/>.</summary>
Cancelled,

/// <summary>An unexpected error occurred.</summary>
Unknown,
}

/// <summary>
/// Result of an emulator boot operation.
/// </summary>
public record EmulatorBootResult
{
public bool Success { get; init; }
public string? Serial { get; init; }
public string? ErrorMessage { get; init; }

/// <summary>
/// Structured error classification. Consumers should switch on this value
/// instead of parsing <see cref="ErrorMessage"/> strings.
/// </summary>
public EmulatorBootErrorKind ErrorKind { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
*REMOVED*static Xamarin.Android.Tools.ProcessUtils.StartProcess(System.Diagnostics.ProcessStartInfo! psi, System.IO.TextWriter? stdout, System.IO.TextWriter? stderr, System.Threading.CancellationToken cancellationToken, System.Action<System.Diagnostics.Process!>? onStarted = null) -> System.Threading.Tasks.Task<int>!
Xamarin.Android.Tools.AdbDeviceInfo
Xamarin.Android.Tools.AdbDeviceInfo.AdbDeviceInfo() -> void
Xamarin.Android.Tools.AdbDeviceInfo.AvdName.get -> string?
Expand Down Expand Up @@ -32,8 +31,8 @@ Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbDeviceType.Device = 0 -> Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbDeviceType.Emulator = 1 -> Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbRunner
Xamarin.Android.Tools.AdbRunner.AdbRunner(string! adbPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null) -> void
Xamarin.Android.Tools.AdbRunner.ListDevicesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AdbDeviceInfo!>!>!
Xamarin.Android.Tools.AdbRunner.AdbRunner(string! adbPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null, System.Action<System.Diagnostics.TraceLevel, string!>? logger = null) -> void
virtual Xamarin.Android.Tools.AdbRunner.ListDevicesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AdbDeviceInfo!>!>!
Xamarin.Android.Tools.AdbRunner.StopEmulatorAsync(string! serial, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.AdbRunner.WaitForDeviceAsync(string? serial = null, System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.ChecksumType
Expand Down Expand Up @@ -145,3 +144,35 @@ Xamarin.Android.Tools.AvdManagerRunner.AvdManagerRunner(string! avdManagerPath,
Xamarin.Android.Tools.AvdManagerRunner.GetOrCreateAvdAsync(string! name, string! systemImage, string? deviceProfile = null, bool force = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.AvdInfo!>!
Xamarin.Android.Tools.AvdManagerRunner.DeleteAvdAsync(string! name, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.AvdManagerRunner.ListAvdsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AvdInfo!>!>!
virtual Xamarin.Android.Tools.AdbRunner.GetShellPropertyAsync(string! serial, string! propertyName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string?>!
virtual Xamarin.Android.Tools.AdbRunner.RunShellCommandAsync(string! serial, string! command, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string?>!
virtual Xamarin.Android.Tools.AdbRunner.RunShellCommandAsync(string! serial, string! command, string![]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string?>!
Xamarin.Android.Tools.EmulatorBootOptions
Xamarin.Android.Tools.EmulatorBootOptions.AdditionalArgs.get -> System.Collections.Generic.List<string!>?
Xamarin.Android.Tools.EmulatorBootOptions.AdditionalArgs.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.BootTimeout.get -> System.TimeSpan
Xamarin.Android.Tools.EmulatorBootOptions.BootTimeout.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.ColdBoot.get -> bool
Xamarin.Android.Tools.EmulatorBootOptions.ColdBoot.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.PollInterval.get -> System.TimeSpan
Xamarin.Android.Tools.EmulatorBootOptions.PollInterval.init -> void
Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.None = 0 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.LaunchFailed = 1 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Timeout = 2 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Cancelled = 3 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Unknown = 4 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootResult
Xamarin.Android.Tools.EmulatorBootResult.ErrorKind.get -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootResult.ErrorKind.init -> void
Xamarin.Android.Tools.EmulatorBootResult.ErrorMessage.get -> string?
Xamarin.Android.Tools.EmulatorBootResult.ErrorMessage.init -> void
Xamarin.Android.Tools.EmulatorBootResult.Serial.get -> string?
Xamarin.Android.Tools.EmulatorBootResult.Serial.init -> void
Xamarin.Android.Tools.EmulatorBootResult.Success.get -> bool
Xamarin.Android.Tools.EmulatorBootResult.Success.init -> void
Xamarin.Android.Tools.EmulatorRunner
Xamarin.Android.Tools.EmulatorRunner.BootEmulatorAsync(string! deviceOrAvdName, Xamarin.Android.Tools.AdbRunner! adbRunner, Xamarin.Android.Tools.EmulatorBootOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.EmulatorBootResult!>!
Xamarin.Android.Tools.EmulatorRunner.EmulatorRunner(string! emulatorPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null, System.Action<System.Diagnostics.TraceLevel, string!>? logger = null) -> void
Xamarin.Android.Tools.EmulatorRunner.ListAvdNamesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<string!>!>!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulator(string! avdName, bool coldBoot = false, System.Collections.Generic.List<string!>? additionalArgs = null) -> System.Diagnostics.Process!
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
*REMOVED*static Xamarin.Android.Tools.ProcessUtils.StartProcess(System.Diagnostics.ProcessStartInfo! psi, System.IO.TextWriter? stdout, System.IO.TextWriter? stderr, System.Threading.CancellationToken cancellationToken, System.Action<System.Diagnostics.Process!>? onStarted = null) -> System.Threading.Tasks.Task<int>!
Xamarin.Android.Tools.AdbDeviceInfo
Xamarin.Android.Tools.AdbDeviceInfo.AdbDeviceInfo() -> void
Xamarin.Android.Tools.AdbDeviceInfo.AvdName.get -> string?
Expand Down Expand Up @@ -32,8 +31,8 @@ Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbDeviceType.Device = 0 -> Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbDeviceType.Emulator = 1 -> Xamarin.Android.Tools.AdbDeviceType
Xamarin.Android.Tools.AdbRunner
Xamarin.Android.Tools.AdbRunner.AdbRunner(string! adbPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null) -> void
Xamarin.Android.Tools.AdbRunner.ListDevicesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AdbDeviceInfo!>!>!
Xamarin.Android.Tools.AdbRunner.AdbRunner(string! adbPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null, System.Action<System.Diagnostics.TraceLevel, string!>? logger = null) -> void
virtual Xamarin.Android.Tools.AdbRunner.ListDevicesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AdbDeviceInfo!>!>!
Xamarin.Android.Tools.AdbRunner.StopEmulatorAsync(string! serial, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.AdbRunner.WaitForDeviceAsync(string? serial = null, System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.ChecksumType
Expand Down Expand Up @@ -145,3 +144,35 @@ Xamarin.Android.Tools.AvdManagerRunner.AvdManagerRunner(string! avdManagerPath,
Xamarin.Android.Tools.AvdManagerRunner.GetOrCreateAvdAsync(string! name, string! systemImage, string? deviceProfile = null, bool force = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.AvdInfo!>!
Xamarin.Android.Tools.AvdManagerRunner.DeleteAvdAsync(string! name, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Xamarin.Android.Tools.AvdManagerRunner.ListAvdsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Xamarin.Android.Tools.AvdInfo!>!>!
virtual Xamarin.Android.Tools.AdbRunner.GetShellPropertyAsync(string! serial, string! propertyName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string?>!
virtual Xamarin.Android.Tools.AdbRunner.RunShellCommandAsync(string! serial, string! command, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string?>!
virtual Xamarin.Android.Tools.AdbRunner.RunShellCommandAsync(string! serial, string! command, string![]! args, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string?>!
Xamarin.Android.Tools.EmulatorBootOptions
Xamarin.Android.Tools.EmulatorBootOptions.AdditionalArgs.get -> System.Collections.Generic.List<string!>?
Xamarin.Android.Tools.EmulatorBootOptions.AdditionalArgs.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.BootTimeout.get -> System.TimeSpan
Xamarin.Android.Tools.EmulatorBootOptions.BootTimeout.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.ColdBoot.get -> bool
Xamarin.Android.Tools.EmulatorBootOptions.ColdBoot.init -> void
Xamarin.Android.Tools.EmulatorBootOptions.PollInterval.get -> System.TimeSpan
Xamarin.Android.Tools.EmulatorBootOptions.PollInterval.init -> void
Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.None = 0 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.LaunchFailed = 1 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Timeout = 2 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Cancelled = 3 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootErrorKind.Unknown = 4 -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootResult
Xamarin.Android.Tools.EmulatorBootResult.ErrorKind.get -> Xamarin.Android.Tools.EmulatorBootErrorKind
Xamarin.Android.Tools.EmulatorBootResult.ErrorKind.init -> void
Xamarin.Android.Tools.EmulatorBootResult.ErrorMessage.get -> string?
Xamarin.Android.Tools.EmulatorBootResult.ErrorMessage.init -> void
Xamarin.Android.Tools.EmulatorBootResult.Serial.get -> string?
Xamarin.Android.Tools.EmulatorBootResult.Serial.init -> void
Xamarin.Android.Tools.EmulatorBootResult.Success.get -> bool
Xamarin.Android.Tools.EmulatorBootResult.Success.init -> void
Xamarin.Android.Tools.EmulatorRunner
Xamarin.Android.Tools.EmulatorRunner.BootEmulatorAsync(string! deviceOrAvdName, Xamarin.Android.Tools.AdbRunner! adbRunner, Xamarin.Android.Tools.EmulatorBootOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.EmulatorBootResult!>!
Xamarin.Android.Tools.EmulatorRunner.EmulatorRunner(string! emulatorPath, System.Collections.Generic.IDictionary<string!, string!>? environmentVariables = null, System.Action<System.Diagnostics.TraceLevel, string!>? logger = null) -> void
Xamarin.Android.Tools.EmulatorRunner.ListAvdNamesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<string!>!>!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulator(string! avdName, bool coldBoot = false, System.Collections.Generic.List<string!>? additionalArgs = null) -> System.Diagnostics.Process!
Loading