diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Runners/EmulatorRunner.cs b/src/Xamarin.Android.Tools.AndroidSdk/Runners/EmulatorRunner.cs new file mode 100644 index 00000000..d745a717 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Runners/EmulatorRunner.cs @@ -0,0 +1,110 @@ +// 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; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Xamarin.Android.Tools +{ + /// + /// Runs Android Emulator commands. + /// + public class EmulatorRunner + { + readonly Func getSdkPath; + readonly Func? getJdkPath; + + public EmulatorRunner (Func getSdkPath) + : this (getSdkPath, null) + { + } + + public EmulatorRunner (Func getSdkPath, Func? getJdkPath) + { + this.getSdkPath = getSdkPath ?? throw new ArgumentNullException (nameof (getSdkPath)); + this.getJdkPath = getJdkPath; + } + + public string? EmulatorPath { + get { + var sdkPath = getSdkPath (); + if (string.IsNullOrEmpty (sdkPath)) + return null; + + var ext = OS.IsWindows ? ".exe" : ""; + var path = Path.Combine (sdkPath, "emulator", "emulator" + ext); + + return File.Exists (path) ? path : null; + } + } + + public bool IsAvailable => EmulatorPath is not null; + + void ConfigureEnvironment (ProcessStartInfo psi) + { + var sdkPath = getSdkPath (); + if (!string.IsNullOrEmpty (sdkPath)) + psi.EnvironmentVariables ["ANDROID_HOME"] = sdkPath; + + var jdkPath = getJdkPath?.Invoke (); + if (!string.IsNullOrEmpty (jdkPath)) + psi.EnvironmentVariables ["JAVA_HOME"] = jdkPath; + } + + public Process StartAvd (string avdName, bool coldBoot = false, string? additionalArgs = null) + { + if (!IsAvailable) + throw new InvalidOperationException ("Android Emulator not found."); + + var args = $"-avd \"{avdName}\""; + if (coldBoot) + args += " -no-snapshot-load"; + if (!string.IsNullOrEmpty (additionalArgs)) + args += " " + additionalArgs; + + var psi = new ProcessStartInfo { + FileName = EmulatorPath!, + Arguments = args, + UseShellExecute = false, + CreateNoWindow = true + }; + ConfigureEnvironment (psi); + + var process = new Process { StartInfo = psi }; + process.Start (); + + return process; + } + + public async Task> ListAvdNamesAsync (CancellationToken cancellationToken = default) + { + if (!IsAvailable) + throw new InvalidOperationException ("Android Emulator not found."); + + var stdout = new StringWriter (); + var psi = new ProcessStartInfo { + FileName = EmulatorPath!, + Arguments = "-list-avds", + UseShellExecute = false, + CreateNoWindow = true + }; + ConfigureEnvironment (psi); + + await ProcessUtils.StartProcess (psi, stdout, null, cancellationToken).ConfigureAwait (false); + + var avds = new List (); + foreach (var line in stdout.ToString ().Split ('\n')) { + var trimmed = line.Trim (); + if (!string.IsNullOrEmpty (trimmed)) + avds.Add (trimmed); + } + + return avds; + } + } +} +