Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Threading.Tasks;

namespace Xamarin.Android.Tools;

/// <summary>
/// Returned by <see cref="EmulatorRunner.LaunchEmulator"/> with enriched launch information.
/// </summary>
public sealed class EmulatorLaunchResult
{
public EmulatorLaunchResult (Process process, string logPath)
{
if (process is null)
throw new System.ArgumentNullException (nameof (process));
Comment thread
rmarinho marked this conversation as resolved.
if (logPath is null)
throw new System.ArgumentNullException (nameof (logPath));
Process = process;
LogPath = logPath;
}
Comment thread
rmarinho marked this conversation as resolved.

/// <summary>The running emulator process.</summary>
public Process Process { get; }

/// <summary>The OS process ID of the emulator process.</summary>
public int Pid => Process.Id;

/// <summary>
/// The emulator console port (e.g., 5554). Populated either from the pre-assigned
/// <c>-ports</c> argument or once <see cref="PortsResolvedAsync"/> completes.
/// </summary>
public int? ConsolePort { get; internal set; }

/// <summary>
/// The emulator ADB port (e.g., 5555). Populated either from the pre-assigned
/// <c>-ports</c> argument or once <see cref="PortsResolvedAsync"/> completes.
/// </summary>
public int? AdbPort { get; internal set; }

/// <summary>
/// The ADB serial for this emulator (e.g., <c>emulator-5554</c>), derived from <see cref="ConsolePort"/>.
/// Returns <c>null</c> until <see cref="ConsolePort"/> is populated.
/// </summary>
public string? Serial => ConsolePort is int p ? $"emulator-{p}" : null;

/// <summary>
/// The path to the emulator log file. Resolved at launch time from the <c>-logfile</c>
/// argument (if specified) or from the <c>ANDROID_AVD_HOME</c> / <c>ANDROID_USER_HOME</c>
/// environment variables, falling back to the AOSP default
/// (<c>~/.android/avd/&lt;name&gt;.avd/emulator.log</c>).
/// </summary>
public string LogPath { get; }

/// <summary>
/// A <see cref="Task"/> that completes when the emulator has reported its console and ADB
/// port assignments via stdout/stderr. If ports were pre-assigned via <c>-ports</c>, this
/// task is already completed.
/// </summary>
internal Task PortsResolvedAsync { get; set; } = Task.CompletedTask;
}
49 changes: 49 additions & 0 deletions src/Xamarin.Android.Tools.AndroidSdk/ProcessUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,55 @@ internal static IEnumerable<string> ExecutableFiles (string executable)
yield return executable;
}

/// <summary>
/// Starts a process without waiting for it to exit (fire-and-forget). Attaches async
/// line-by-line stdout/stderr callbacks, starts the process, and begins async reads.
/// The caller is responsible for the process lifetime.
/// </summary>
/// <param name="process">A <see cref="Process"/> whose <see cref="ProcessStartInfo"/> is already configured.</param>
/// <param name="onOutputLine">Optional callback invoked for each line written to stdout.</param>
/// <param name="onErrorLine">Optional callback invoked for each line written to stderr.</param>
/// <param name="onExited">Optional callback invoked with the exit code when the process exits.</param>
/// <exception cref="InvalidOperationException">Thrown when the process fails to start.</exception>
internal static void StartFireAndForget (
Process process,
Action<string>? onOutputLine,
Action<string>? onErrorLine,
Action<int>? onExited = null)
{
if (onOutputLine != null) {
process.OutputDataReceived += (_, e) => {
if (e.Data != null)
onOutputLine (e.Data);
};
}
if (onErrorLine != null) {
process.ErrorDataReceived += (_, e) => {
if (e.Data != null)
onErrorLine (e.Data);
};
}

if (onExited != null) {
process.EnableRaisingEvents = true;
process.Exited += (_, _) => {
int exitCode;
try { exitCode = process.ExitCode; } catch { exitCode = -1; }
onExited (exitCode);
};
}

if (!process.Start ()) {
process.Dispose ();
throw new InvalidOperationException ($"Failed to start process '{process.StartInfo.FileName}'.");
}

if (process.StartInfo.RedirectStandardOutput)
process.BeginOutputReadLine ();
if (process.StartInfo.RedirectStandardError)
process.BeginErrorReadLine ();
}

/// <summary>Checks if running as Administrator (Windows) or root (macOS/Linux).</summary>
public static bool IsElevated ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@ 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!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulator(string! avdName, bool coldBoot = false, int? consolePort = null, int? adbPort = null, string? logFile = null, System.Collections.Generic.List<string!>? additionalArgs = null) -> Xamarin.Android.Tools.EmulatorLaunchResult!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulatorAsync(string! avdName, bool coldBoot = false, int? consolePort = null, int? adbPort = null, string? logFile = null, System.Collections.Generic.List<string!>? additionalArgs = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.EmulatorLaunchResult!>!
Xamarin.Android.Tools.EmulatorLaunchResult
Xamarin.Android.Tools.EmulatorLaunchResult.EmulatorLaunchResult(System.Diagnostics.Process! process, string! logPath) -> void
Xamarin.Android.Tools.EmulatorLaunchResult.AdbPort.get -> int?
Xamarin.Android.Tools.EmulatorLaunchResult.ConsolePort.get -> int?
Xamarin.Android.Tools.EmulatorLaunchResult.LogPath.get -> string!
Xamarin.Android.Tools.EmulatorLaunchResult.Pid.get -> int
Xamarin.Android.Tools.EmulatorLaunchResult.Process.get -> System.Diagnostics.Process!
Xamarin.Android.Tools.EmulatorLaunchResult.Serial.get -> string?
Xamarin.Android.Tools.AdbPortRule
Xamarin.Android.Tools.AdbPortRule.AdbPortRule(Xamarin.Android.Tools.AdbPortSpec! Remote, Xamarin.Android.Tools.AdbPortSpec! Local) -> void
Xamarin.Android.Tools.AdbPortRule.Local.get -> Xamarin.Android.Tools.AdbPortSpec!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@ 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!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulator(string! avdName, bool coldBoot = false, int? consolePort = null, int? adbPort = null, string? logFile = null, System.Collections.Generic.List<string!>? additionalArgs = null) -> Xamarin.Android.Tools.EmulatorLaunchResult!
Xamarin.Android.Tools.EmulatorRunner.LaunchEmulatorAsync(string! avdName, bool coldBoot = false, int? consolePort = null, int? adbPort = null, string? logFile = null, System.Collections.Generic.List<string!>? additionalArgs = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Xamarin.Android.Tools.EmulatorLaunchResult!>!
Xamarin.Android.Tools.EmulatorLaunchResult
Xamarin.Android.Tools.EmulatorLaunchResult.EmulatorLaunchResult(System.Diagnostics.Process! process, string! logPath) -> void
Xamarin.Android.Tools.EmulatorLaunchResult.AdbPort.get -> int?
Xamarin.Android.Tools.EmulatorLaunchResult.ConsolePort.get -> int?
Xamarin.Android.Tools.EmulatorLaunchResult.LogPath.get -> string!
Xamarin.Android.Tools.EmulatorLaunchResult.Pid.get -> int
Xamarin.Android.Tools.EmulatorLaunchResult.Process.get -> System.Diagnostics.Process!
Xamarin.Android.Tools.EmulatorLaunchResult.Serial.get -> string?
Xamarin.Android.Tools.AdbPortRule
Xamarin.Android.Tools.AdbPortRule.AdbPortRule(Xamarin.Android.Tools.AdbPortSpec! Remote, Xamarin.Android.Tools.AdbPortSpec! Local) -> void
Xamarin.Android.Tools.AdbPortRule.Local.get -> Xamarin.Android.Tools.AdbPortSpec!
Expand Down
Loading