diff --git a/UET/uet/Commands/Android/AndroidCommand.cs b/UET/uet/Commands/Android/AndroidCommand.cs
new file mode 100644
index 00000000..543be0fe
--- /dev/null
+++ b/UET/uet/Commands/Android/AndroidCommand.cs
@@ -0,0 +1,14 @@
+namespace UET.Commands.Android
+{
+    using System.CommandLine;
+
+    internal sealed class AndroidCommand
+    {
+        public static Command CreateAndroidCommand()
+        {
+            var command = new Command("android", "Various utilities for Android development.");
+            command.AddCommand(AndroidKeepWirelessEnabledCommand.CreateAndroidKeepWirelessEnabledCommand());
+            return command;
+        }
+    }
+}
diff --git a/UET/uet/Commands/Android/AndroidKeepWirelessEnabledCommand.cs b/UET/uet/Commands/Android/AndroidKeepWirelessEnabledCommand.cs
new file mode 100644
index 00000000..d04e1457
--- /dev/null
+++ b/UET/uet/Commands/Android/AndroidKeepWirelessEnabledCommand.cs
@@ -0,0 +1,361 @@
+namespace UET.Commands.Android
+{
+    using Microsoft.Extensions.Logging;
+    using System.CommandLine;
+    using UET.Commands.EngineSpec;
+    using System.CommandLine.Invocation;
+    using System.Security.Cryptography;
+    using System.Security.Cryptography.X509Certificates;
+    using System.Text.RegularExpressions;
+    using System.Threading.Tasks;
+    using Redpoint.Uet.SdkManagement;
+    using Redpoint.Uet.Workspace;
+    using Redpoint.Uet.BuildPipeline.Executors.Engine;
+    using Redpoint.Concurrency;
+    using Redpoint.Uet.CommonPaths;
+    using Microsoft.Extensions.DependencyInjection;
+    using Redpoint.Uet.Workspace.Reservation;
+    using Redpoint.Reservation;
+    using Redpoint.ProcessExecution;
+    using System.Globalization;
+    using System.Text;
+    using Redpoint.PathResolution;
+
+    internal sealed class AndroidKeepWirelessEnabledCommand
+    {
+        internal sealed class Options
+        {
+            public Option<EngineSpec> Engine;
+            public Option<bool> Once;
+
+            public Options()
+            {
+                Engine = new Option<EngineSpec>(
+                    "--engine",
+                    description: "The engine which defines the Android SDK version to use.",
+                    parseArgument: EngineSpec.ParseEngineSpecContextless(),
+                    isDefault: true);
+                Engine.AddAlias("-e");
+                Engine.Arity = ArgumentArity.ExactlyOne;
+
+                Once = new Option<bool>("--once")
+                {
+                    Description = "If set, this command performs the action once instead of continously running in the background. This option should be used when scheduling this operation on a build server (rather than running as a background service).",
+                };
+                Once.AddAlias("-o");
+            }
+        }
+
+        public static Command CreateAndroidKeepWirelessEnabledCommand()
+        {
+            var options = new Options();
+            var command = new Command("keep-wireless-enabled", "Automatically keep 'Wireless debugging' enabled on connected Android devices.");
+            command.AddAllOptions(options);
+            command.AddCommonHandler<CreateAndroidKeepWirelessEnabledCommandInstance>(options);
+            return command;
+        }
+
+        private sealed class CreateAndroidKeepWirelessEnabledCommandInstance : ICommandInstance
+        {
+            private readonly ILogger<CreateAndroidKeepWirelessEnabledCommandInstance> _logger;
+            private readonly ILocalSdkManager _localSdkManager;
+            private readonly IEngineWorkspaceProvider _engineWorkspaceProvider;
+            private readonly IServiceProvider _serviceProvider;
+            private readonly IProcessExecutor _processExecutor;
+            private readonly IPathResolver _pathResolver;
+            private readonly ILoopbackPortReservationManager _loopbackPortReservationManager;
+            private readonly Options _options;
+
+            public CreateAndroidKeepWirelessEnabledCommandInstance(
+                ILogger<CreateAndroidKeepWirelessEnabledCommandInstance> logger,
+                ILocalSdkManager localSdkManager,
+                IEngineWorkspaceProvider engineWorkspaceProvider,
+                IServiceProvider serviceProvider,
+                IReservationManagerFactory reservationManagerFactory,
+                IProcessExecutor processExecutor,
+                IPathResolver pathResolver,
+                Options options)
+            {
+                _logger = logger;
+                _localSdkManager = localSdkManager;
+                _engineWorkspaceProvider = engineWorkspaceProvider;
+                _serviceProvider = serviceProvider;
+                _processExecutor = processExecutor;
+                _pathResolver = pathResolver;
+                _loopbackPortReservationManager = reservationManagerFactory.CreateLoopbackPortReservationManager();
+                _options = options;
+            }
+
+            public async Task<int> ExecuteAsync(InvocationContext context)
+            {
+                if (!OperatingSystem.IsWindows())
+                {
+                    _logger.LogError("This command is not currently supported on non-Windows platforms.");
+                    return 1;
+                }
+
+                var engine = context.ParseResult.GetValueForOption(_options.Engine)!;
+                var once = context.ParseResult.GetValueForOption(_options.Once)!;
+
+                var engineSpec = engine.ToBuildEngineSpecification("keep-wireless-enabled");
+
+                await using ((await _engineWorkspaceProvider.GetEngineWorkspace(
+                    engineSpec,
+                    string.Empty,
+                    context.GetCancellationToken()).ConfigureAwait(false))
+                        .AsAsyncDisposable(out var engineWorkspace)
+                        .ConfigureAwait(false))
+                {
+                    var packagePath = UetPaths.UetDefaultWindowsSdkStoragePath;
+                    Directory.CreateDirectory(packagePath);
+                    var envVars = await _localSdkManager.SetupEnvironmentForSdkSetups(
+                        engine.Path!,
+                        packagePath,
+                        _serviceProvider.GetServices<ISdkSetup>().ToHashSet(),
+                        context.GetCancellationToken()).ConfigureAwait(false);
+
+                    var adbkeyPath = Path.Combine(
+                        Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+                        ".android",
+                        "adbkey");
+                    var adbkeyPubPath = Path.Combine(
+                        Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+                        ".android",
+                        "adbkey.pub");
+
+                    // @note: This is necessary on build servers where users and machines might vary over time.
+                    var privateKey =
+                        """
+                        -----BEGIN PRIVATE KEY-----
+                        MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDxzwYvn11zEXM4
+                        yuWLzGp+C0XeQkUU6j3sJsK0bw7LRjSyR1vjAkoUJP1WMI5fy7OzzBPsg81YcODg
+                        Y+DnYpaJLcG8R0GWTsZbMTG29VVraRcmDz7IUyLhzn7dA5Hc9+4gGZLfg5YmO0Dd
+                        MViPAnJ0gdhqYbH4t9m3l1v5fDb+i3BAUKc5pSGw5jg4mRDWNj+vreWKHWx1i4bR
+                        sOJbxKsTe9PXznTHwRQwtkazxPR994GPIe/lYOxYaYQqqgR0nhdH5nMJgPDPeSOD
+                        bq9YeCPRw9yYUMa+n7cnSJ2ZMndMqiqHiYkDKGeuH2lCvsf8IT2QZD01+JEXbEKD
+                        hK5zH/8dAgMBAAECggEABY5B4GC7OyxUtyKURRLAhJ5aL9nab/lUzGL0mMQvdRSr
+                        G8h/cDcKgC18Y5lQgBt0SMaA06+QjX5kcEtjLLXLayHxwFNrypoLPSejcoZu/L2I
+                        ol95zAz68XC24fmVxZutrS+hPADwN3cnjZ13YSvHeO1NzV3qwqHovurbml8T/WOn
+                        1P+ISB4jtBuLeoRMfDKSZZDfljnaS+fk1ExRWFBMdbd2EqXQoII74oXjUL+6WblP
+                        ZAMUduB02cLxaPwndQ8UYcxR/PGq+hPZza8k8yfJ8MXklkl/QtxOcljOe/Om7+5R
+                        DKMD6dB6Jmd9QlfcJ+8U53/F4VXxYXja0YQBjJOV2QKBgQD49GZhaG+MsLKtepfD
+                        leuq1hg+Rwu83yu7hyO5WKOHu82RZQjvizNSnxKx0NXv0L32cVLP0G3zDXI98lmk
+                        C1/r7ExUOC2ECV95yOrXPjkJoFN3lkxKd0myxalUypfADtXqRMPtfjEnwcpGPwig
+                        wsdG+lA5h7r+ajfT1RjIOkfV9QKBgQD4ptqKkBCBtF/8eMMIkNODL/3C9SCktHob
+                        QCfOlZ2K4QYQyRK+2+yD2jBhpWxjK7BHhfK/WcawUcJT9f9KD3f8B4mqUADEweER
+                        eWh90O3yRYd0k35V4SVBbuIAYRdwzHiS5WFVKYQyuYRUpRP1h5pXZWGarYxLqtO6
+                        vhe7G/YjiQKBgDUFqIB6g7eNMqDsCUKovYanDobFDuTtCx1njN4+2KViBEhBIoQS
+                        O54PLyYb+lSXOr4wKJkGJUSsynYTFbBwk79llmQhiuAiNulzN0EciX1ZXi2MHzeE
+                        7HdczdG3TFalUj4Q40HDrKhxB6mqZyYGFfcx/MAj/lmNOdKuAhczAnW5AoGAQyet
+                        NmcaTi2NDv7+jb2vomq/unvByToFEH8PQTgfSHbl0Hq92VZEVogDMRwgXdhaz7ZZ
+                        jVyN0OkD9vEldbcfzK2sfJcG3h0O0E1d7z0SRrCImO+M21znVvi/iSKv1gMjPWk+
+                        FGYWEi0QlFvRPCrXgGsdJU1h6r3EWVclyZ8PpyECgYEA87IZZCYXgvsGUz9sQSth
+                        OQKEBE0OWnDHs1ZrMs0lfxKImPWrbj7CdKO5QlJADMwyH6nEL3ZVb8ETDK6T1VZa
+                        Fbs6Pe7QRnoJucsM01pC79w0UtnNCHdXEEVeAbdKJmYgbGYU0Uvcsnz3M45lHsCy
+                        T758Kl3IJGbsGU3C/T6WQUM=
+                        -----END PRIVATE KEY-----
+                        """.Replace("\r\n", "\n", StringComparison.Ordinal);
+                    var publicKey =
+                        """
+                        QAAAAMtEwZUd/x9zroSDQmwXkfg1PWSQPSH8x75CaR+uZygDiYmHKqpMdzKZnUgnt5++xlCY3MPRI3hYr26DI3nP8IAJc+ZHF550BKoqhGlY7GDl7yGPgfd99MSzRrYwFMHHdM7X03sTq8Rb4rDRhot1bB2K5a2vPzbWEJk4OOawIaU5p1BAcIv+Nnz5W5e32bf4sWFq2IF0cgKPWDHdQDsmloPfkhkg7vfckQPdfs7hIlPIPg8mF2lrVfW2MTFbxk6WQUe8wS2JlmLn4GPg4HBYzYPsE8yzs8tfjjBW/SQUSgLjW0eyNEbLDm+0wibsPeoURULeRQt+asyL5co4cxFzXZ8vBs/xr6bxkIoKf8rjRNqozj5iXXPhLiW0euQNbFyxwMaGU2j/5phWQ3M5jyUWhsJ4iZb1+m8qyIqgjxfVnnBQlJZdH9ytkERWRx905AE2ITStDL5JUFYrl7hlNgArhdhUosBpVbl/a1DbP4EDaqeFhgfuCy77/I0SoAm5XgMnfqIv3w+tBR8kPGh1sJKxkICXqGK5Zhiyag3BIfLz3eskB1XZdrFn8JrvRsj3ZQwpxDGTtlw2lsq3FbKlVWMucyqbNTnBiuI/Vd10HqY6oaLvGKMbbwnr8eQS3x/0T0vn0b30KEibLAjPwaomuFWq6UdvIGY1RWlEqS93PUtPpIki3vqQkQEAAQA= uet-well-known-key
+                        """.Replace("\r\n", "\n", StringComparison.Ordinal);
+
+                    var existingPrivateKey = File.Exists(adbkeyPath) ? File.ReadAllText(adbkeyPath) : string.Empty;
+                    var existingPublicKey = File.Exists(adbkeyPubPath) ? File.ReadAllText(adbkeyPubPath) : string.Empty;
+
+                    if (existingPrivateKey != privateKey || existingPublicKey != publicKey)
+                    {
+                        _logger.LogInformation("Forcing adbkey to be a well-known key to avoid USB re-authorization prompts...");
+                        File.WriteAllText(adbkeyPath, privateKey);
+                        File.WriteAllText(adbkeyPubPath, publicKey);
+
+                        _logger.LogInformation("Terminating any existing 'adb' processes to ensure ADB server sees new public/private keypair...");
+                        await _processExecutor.ExecuteAsync(
+                            new ProcessSpecification
+                            {
+                                FilePath = await _pathResolver.ResolveBinaryPath("taskkill").ConfigureAwait(false),
+                                Arguments = ["/f", "/im", "adb.exe"]
+                            },
+                            CaptureSpecification.Passthrough,
+                            CancellationToken.None).ConfigureAwait(false);
+                    }
+
+                    await using ((await _loopbackPortReservationManager.ReserveAsync().ConfigureAwait(false))
+                        .AsAsyncDisposable(out var loopbackPort)
+                        .ConfigureAwait(false))
+                    {
+                        var adbPath = Path.Combine(
+                            envVars["ANDROID_HOME"],
+                            "platform-tools",
+                            "adb.exe");
+
+                        try
+                        {
+                            do
+                            {
+                                _logger.LogInformation($"Listing devices...");
+                                var devicesStringBuilder = new StringBuilder();
+                                await _processExecutor.ExecuteAsync(
+                                    new ProcessSpecification
+                                    {
+                                        FilePath = adbPath,
+                                        Arguments = ["devices", "-l"],
+                                        EnvironmentVariables = envVars,
+                                    },
+                                    CaptureSpecification.CreateFromSanitizedStdoutStringBuilder(devicesStringBuilder),
+                                    context.GetCancellationToken());
+                                var devicesOutput = devicesStringBuilder.ToString()
+                                    .Replace("List of devices attached", "", StringComparison.OrdinalIgnoreCase)
+                                    .Trim();
+                                var devices = devicesOutput
+                                    .Replace("\r\n", "\n", StringComparison.Ordinal)
+                                    .Split("\n", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+
+                                _logger.LogInformation($"Found {devices.Length} devices.");
+                                if (!string.IsNullOrWhiteSpace(devicesOutput))
+                                {
+                                    _logger.LogInformation(devicesOutput);
+                                }
+                                foreach (var deviceEntry in devices)
+                                {
+                                    var device = deviceEntry.Split("  ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+                                    if (device.Length < 2)
+                                    {
+                                        continue;
+                                    }
+
+                                    var deviceId = device[0];
+                                    var details = device[1];
+                                    if (!deviceId.Contains(':', StringComparison.Ordinal) &&
+                                        !deviceId.Contains("._tcp", StringComparison.Ordinal))
+                                    {
+                                        // This is a USB connected device.
+
+                                        _logger.LogInformation($"Querying wlan0 IP address of '{deviceId}'...");
+                                        var addressStringBuilder = new StringBuilder();
+                                        await _processExecutor.ExecuteAsync(
+                                            new ProcessSpecification
+                                            {
+                                                FilePath = adbPath,
+                                                Arguments = ["-s", deviceId, "shell", "ip", "-o", "-4", "address", "show", "dev", "wlan0"],
+                                                EnvironmentVariables = envVars,
+                                            },
+                                            CaptureSpecification.CreateFromSanitizedStdoutStringBuilder(addressStringBuilder),
+                                            context.GetCancellationToken());
+                                        var addressMatch = new Regex("inet ([0-9\\.]+)/").Match(addressStringBuilder.ToString());
+                                        if (!addressMatch.Success)
+                                        {
+                                            _logger.LogWarning($"Unable to find wlan0 address for {deviceId}:\n{addressStringBuilder}");
+                                            continue;
+                                        }
+
+                                        var address = addressMatch.Groups[1].Value;
+                                        _logger.LogInformation($"The device's wireless IP address is: {address}");
+
+                                        // Check if we can already connect - we don't want to restart adbd if it's already in TCP/IP mode
+                                        // because that may interfere with any builds that are currently using it.
+                                        var needsConnection = false;
+                                        _logger.LogInformation($"Attempting to connect to device on '{address}:5555'...");
+                                        var connectStringBuilder = new StringBuilder();
+                                        await _processExecutor.ExecuteAsync(
+                                            new ProcessSpecification
+                                            {
+                                                FilePath = adbPath,
+                                                Arguments = ["connect", $"{address}:5555"],
+                                                EnvironmentVariables = envVars,
+                                            },
+                                            CaptureSpecification.CreateFromSanitizedStdoutStringBuilder(connectStringBuilder),
+                                            context.GetCancellationToken());
+                                        _logger.LogInformation(connectStringBuilder.ToString());
+
+                                        if (connectStringBuilder.ToString().Contains("cannot connect", StringComparison.OrdinalIgnoreCase))
+                                        {
+                                            needsConnection = true;
+                                        }
+                                        else if (connectStringBuilder.ToString().Contains("already connected", StringComparison.OrdinalIgnoreCase))
+                                        {
+                                            // Make sure the connection is actually usable.
+                                            var testExitCode = await _processExecutor.ExecuteAsync(
+                                                new ProcessSpecification
+                                                {
+                                                    FilePath = adbPath,
+                                                    Arguments = ["-s", deviceId, "shell", "echo", "wifi connected"],
+                                                    EnvironmentVariables = envVars,
+                                                },
+                                                CaptureSpecification.Passthrough,
+                                                context.GetCancellationToken());
+                                            if (testExitCode != 0)
+                                            {
+                                                needsConnection = true;
+                                            }
+                                        }
+
+                                        if (needsConnection)
+                                        {
+                                            _logger.LogInformation($"Disconnecting from any device on '{address}:5555' if it's already in the device list...");
+                                            await _processExecutor.ExecuteAsync(
+                                                new ProcessSpecification
+                                                {
+                                                    FilePath = adbPath,
+                                                    Arguments = ["disconnect", $"{address}:5555"],
+                                                    EnvironmentVariables = envVars,
+                                                },
+                                                CaptureSpecification.Passthrough,
+                                                context.GetCancellationToken());
+
+                                            // We can't connect to the device on it's wireless IP address, so we need to switch to TCP/IP mode.
+                                            _logger.LogInformation($"Enabling TCP/IP connection on '{deviceId}'...");
+                                            await _processExecutor.ExecuteAsync(
+                                                new ProcessSpecification
+                                                {
+                                                    FilePath = adbPath,
+                                                    Arguments = ["-s", deviceId, "tcpip", "5555"],
+                                                    EnvironmentVariables = envVars,
+                                                },
+                                                CaptureSpecification.Passthrough,
+                                                context.GetCancellationToken());
+
+                                            _logger.LogInformation($"Attempting to connect to device on '{address}:5555'...");
+                                            connectStringBuilder = new StringBuilder();
+                                            await _processExecutor.ExecuteAsync(
+                                                new ProcessSpecification
+                                                {
+                                                    FilePath = adbPath,
+                                                    Arguments = ["connect", $"{address}:5555"],
+                                                    EnvironmentVariables = envVars,
+                                                },
+                                                CaptureSpecification.CreateFromSanitizedStdoutStringBuilder(connectStringBuilder),
+                                                context.GetCancellationToken());
+                                            _logger.LogInformation(connectStringBuilder.ToString());
+                                            if (connectStringBuilder.ToString().Contains("cannot connect", StringComparison.OrdinalIgnoreCase))
+                                            {
+                                                _logger.LogError($"Device '{deviceId}' failed to switch into TCP/IP mode.");
+                                            }
+                                        }
+                                        else
+                                        {
+                                            _logger.LogInformation($"Device '{deviceId}' is already in TCP/IP mode.");
+                                        }
+                                    }
+                                }
+
+                                if (!once)
+                                {
+                                    await Task.Delay(2000, context.GetCancellationToken()).ConfigureAwait(false);
+                                }
+                            }
+                            while (!once && !context.GetCancellationToken().IsCancellationRequested);
+                        }
+                        catch (OperationCanceledException) when (context.GetCancellationToken().IsCancellationRequested)
+                        {
+                            // Expected.
+                        }
+                    }
+                }
+
+                _logger.LogInformation("Android 'keep-wireless-enabled' command finished.");
+                return 0;
+            }
+        }
+    }
+}
diff --git a/UET/uet/Commands/Build/BuildCommand.cs b/UET/uet/Commands/Build/BuildCommand.cs
index 36a8dc03..0ce82085 100644
--- a/UET/uet/Commands/Build/BuildCommand.cs
+++ b/UET/uet/Commands/Build/BuildCommand.cs
@@ -392,55 +392,11 @@ public async Task<int> ExecuteAsync(InvocationContext context)
                 _logger.LogInformation($"--plugin-version-name:           {pluginVersionName}");
                 _logger.LogInformation($"--plugin-version-number:         {pluginVersionNumber}");
 
-                BuildEngineSpecification engineSpec;
-                switch (engine.Type)
-                {
-                    case EngineSpecType.UEFSPackageTag:
-                        engineSpec = BuildEngineSpecification.ForUEFSPackageTag(engine.UEFSPackageTag!);
-                        break;
-                    case EngineSpecType.SESNetworkShare:
-                        engineSpec = BuildEngineSpecification.ForSESNetworkShare(engine.SESNetworkShare!);
-                        break;
-                    case EngineSpecType.RemoteZfs:
-                        engineSpec = BuildEngineSpecification.ForRemoteZfs(engine.RemoteZfs!);
-                        break;
-                    case EngineSpecType.Version:
-                        engineSpec = BuildEngineSpecification.ForVersionWithPath(engine.Version!, engine.Path!);
-                        break;
-                    case EngineSpecType.Path:
-                        engineSpec = BuildEngineSpecification.ForAbsolutePath(engine.Path!);
-                        break;
-                    case EngineSpecType.GitCommit:
-                        engineSpec = BuildEngineSpecification.ForGitCommitWithZips(
-                            engine.GitUrl!,
-                            engine.GitCommit!,
-                            engine.ZipLayers,
-                            isEngineBuild: false);
-                        break;
-                    case EngineSpecType.SelfEngineByBuildConfig:
-                        var engineDistribution = distribution!.Distribution as BuildConfigEngineDistribution;
-                        var repositoryUrl = engineDistribution!.Source.Repository;
-                        if (!repositoryUrl.Contains("://", StringComparison.Ordinal))
-                        {
-                            var shortSshUrlRegex = new Regex("^(.+@)*([\\w\\d\\.]+):(.*)$");
-                            var shortSshUrlMatch = shortSshUrlRegex.Match(repositoryUrl);
-                            if (shortSshUrlMatch.Success)
-                            {
-                                repositoryUrl = $"ssh://{shortSshUrlMatch.Groups[1].Value}{shortSshUrlMatch.Groups[2].Value}/{shortSshUrlMatch.Groups[3].Value}";
-                            }
-                        }
-                        // @note: This will round trip to ci-build as EngineSpecType.GitCommit
-                        engineSpec = BuildEngineSpecification.ForGitCommitWithZips(
-                            repositoryUrl,
-                            engineDistribution.Source.Ref,
-                            engineDistribution.Source.ConsoleZips,
-                            isEngineBuild: true,
-                            windowsSharedGitCachePath: windowsSharedGitCachePath,
-                            macSharedGitCachePath: macSharedGitCachePath);
-                        break;
-                    default:
-                        throw new NotSupportedException($"The EngineSpecType {engine.Type} is not supported by the 'build' command.");
-                }
+                var engineSpec = engine.ToBuildEngineSpecification(
+                    "build",
+                    distribution,
+                    windowsSharedGitCachePath,
+                    macSharedGitCachePath);
 
                 // @note: We need the build executor to get the pipeline ID, which is also used as an input to compute the derived storage path that's specific for this build.
                 var executor = executorName switch
diff --git a/UET/uet/Commands/InstallSdks/InstallSdksCommand.cs b/UET/uet/Commands/InstallSdks/InstallSdksCommand.cs
index bd872068..44085b15 100644
--- a/UET/uet/Commands/InstallSdks/InstallSdksCommand.cs
+++ b/UET/uet/Commands/InstallSdks/InstallSdksCommand.cs
@@ -85,7 +85,7 @@ public async Task<int> ExecuteAsync(InvocationContext context)
 
                 if (engine.Path == null)
                 {
-                    _logger.LogError("You must specify a local engine (by version number or by path) in order ot use the 'install-sdks' command.");
+                    _logger.LogError("You must specify a local engine (by version number or by path) in order to use the 'install-sdks' command.");
                     return 1;
                 }
 
diff --git a/UET/uet/Commands/Internal/CIBuild/CIBuildCommand.cs b/UET/uet/Commands/Internal/CIBuild/CIBuildCommand.cs
index e64a0ef3..2d64d9aa 100644
--- a/UET/uet/Commands/Internal/CIBuild/CIBuildCommand.cs
+++ b/UET/uet/Commands/Internal/CIBuild/CIBuildCommand.cs
@@ -124,38 +124,11 @@ await _storageManagement.AutoPurgeStorageAsync(
                     return 1;
                 }
 
-                BuildEngineSpecification engineSpec;
-                switch (engine.Type)
-                {
-                    case EngineSpecType.UEFSPackageTag:
-                        engineSpec = BuildEngineSpecification.ForUEFSPackageTag(engine.UEFSPackageTag!);
-                        break;
-                    case EngineSpecType.SESNetworkShare:
-                        engineSpec = BuildEngineSpecification.ForSESNetworkShare(engine.SESNetworkShare!);
-                        break;
-                    case EngineSpecType.RemoteZfs:
-                        engineSpec = BuildEngineSpecification.ForRemoteZfs(engine.RemoteZfs!);
-                        break;
-                    case EngineSpecType.Version:
-                        engineSpec = BuildEngineSpecification.ForVersionWithPath(engine.Version!, engine.Path!);
-                        break;
-                    case EngineSpecType.Path:
-                        engineSpec = BuildEngineSpecification.ForAbsolutePath(engine.Path!);
-                        break;
-                    case EngineSpecType.GitCommit:
-                        engineSpec = BuildEngineSpecification.ForGitCommitWithZips(
-                            engine.GitUrl!,
-                            engine.GitCommit!,
-                            engine.ZipLayers,
-                            isEngineBuild: buildJson.IsEngineBuild,
-                            windowsSharedGitCachePath: engine.WindowsSharedGitCachePath,
-                            macSharedGitCachePath: engine.MacSharedGitCachePath);
-                        break;
-                    case EngineSpecType.SelfEngineByBuildConfig:
-                        throw new InvalidOperationException("EngineSpec.TryParseEngineSpecExact should not be able to return EngineSpecType.SelfEngineByBuildConfig");
-                    default:
-                        throw new NotSupportedException($"The EngineSpecType {engine.Type} is not supported by the 'ci-build' command.");
-                }
+                var engineSpec = engine.ToBuildEngineSpecification(
+                    "ci-build",
+                    null,
+                    engine.WindowsSharedGitCachePath,
+                    engine.MacSharedGitCachePath);
 
                 IBuildNodeExecutor executor;
                 switch (executorName)
diff --git a/UET/uet/Commands/Internal/CMakeUbaServer/CMakeUbaServerCommand.cs b/UET/uet/Commands/Internal/CMakeUbaServer/CMakeUbaServerCommand.cs
index dd064b6a..acc82882 100644
--- a/UET/uet/Commands/Internal/CMakeUbaServer/CMakeUbaServerCommand.cs
+++ b/UET/uet/Commands/Internal/CMakeUbaServer/CMakeUbaServerCommand.cs
@@ -85,34 +85,7 @@ public async Task<int> ExecuteAsync(InvocationContext context)
             {
                 var engine = context.ParseResult.GetValueForOption(_options.Engine)!;
 
-                BuildEngineSpecification engineSpec;
-                switch (engine.Type)
-                {
-                    case EngineSpecType.UEFSPackageTag:
-                        engineSpec = BuildEngineSpecification.ForUEFSPackageTag(engine.UEFSPackageTag!);
-                        break;
-                    case EngineSpecType.SESNetworkShare:
-                        engineSpec = BuildEngineSpecification.ForSESNetworkShare(engine.SESNetworkShare!);
-                        break;
-                    case EngineSpecType.RemoteZfs:
-                        engineSpec = BuildEngineSpecification.ForRemoteZfs(engine.RemoteZfs!);
-                        break;
-                    case EngineSpecType.Version:
-                        engineSpec = BuildEngineSpecification.ForVersionWithPath(engine.Version!, engine.Path!);
-                        break;
-                    case EngineSpecType.Path:
-                        engineSpec = BuildEngineSpecification.ForAbsolutePath(engine.Path!);
-                        break;
-                    case EngineSpecType.GitCommit:
-                        engineSpec = BuildEngineSpecification.ForGitCommitWithZips(
-                            engine.GitUrl!,
-                            engine.GitCommit!,
-                            engine.ZipLayers,
-                            isEngineBuild: false);
-                        break;
-                    default:
-                        throw new NotSupportedException($"The EngineSpecType {engine.Type} is not supported by the 'cmake-uba-server' command.");
-                }
+                var engineSpec = engine.ToBuildEngineSpecification("cmake-uba-server");
 
                 await using ((await _engineWorkspaceProvider.GetEngineWorkspace(
                     engineSpec,
diff --git a/UET/uet/Commands/ParameterSpec/EngineSpec.cs b/UET/uet/Commands/ParameterSpec/EngineSpec.cs
index 2ef03cbe..2015f11e 100644
--- a/UET/uet/Commands/ParameterSpec/EngineSpec.cs
+++ b/UET/uet/Commands/ParameterSpec/EngineSpec.cs
@@ -1,6 +1,7 @@
 namespace UET.Commands.EngineSpec
 {
     using Redpoint.Registry;
+    using Redpoint.Uet.BuildPipeline.Executors;
     using Redpoint.Uet.Configuration.Engine;
     using Redpoint.Uet.Configuration.Project;
     using System;
@@ -540,5 +541,61 @@ public override string ToString()
 
             return OriginalSpec;
         }
+
+        public BuildEngineSpecification ToBuildEngineSpecification(
+            string commandName,
+            DistributionSpec? distributionSpec = null,
+            string? windowsSharedGitCachePath = null,
+            string? macSharedGitCachePath = null)
+        {
+            switch (Type)
+            {
+                case EngineSpecType.UEFSPackageTag:
+                    return BuildEngineSpecification.ForUEFSPackageTag(UEFSPackageTag!);
+                case EngineSpecType.SESNetworkShare:
+                    return BuildEngineSpecification.ForSESNetworkShare(SESNetworkShare!);
+                case EngineSpecType.RemoteZfs:
+                    return BuildEngineSpecification.ForRemoteZfs(RemoteZfs!);
+                case EngineSpecType.Version:
+                    return BuildEngineSpecification.ForVersionWithPath(Version!, Path!);
+                case EngineSpecType.Path:
+                    return BuildEngineSpecification.ForAbsolutePath(Path!);
+                case EngineSpecType.GitCommit:
+                    return BuildEngineSpecification.ForGitCommitWithZips(
+                        GitUrl!,
+                        GitCommit!,
+                        ZipLayers,
+                        isEngineBuild: false);
+                case EngineSpecType.SelfEngineByBuildConfig:
+                    if (distributionSpec != null)
+                    {
+                        var engineDistribution = distributionSpec!.Distribution as BuildConfigEngineDistribution;
+                        var repositoryUrl = engineDistribution!.Source.Repository;
+                        if (!repositoryUrl.Contains("://", StringComparison.Ordinal))
+                        {
+                            var shortSshUrlRegex = new Regex("^(.+@)*([\\w\\d\\.]+):(.*)$");
+                            var shortSshUrlMatch = shortSshUrlRegex.Match(repositoryUrl);
+                            if (shortSshUrlMatch.Success)
+                            {
+                                repositoryUrl = $"ssh://{shortSshUrlMatch.Groups[1].Value}{shortSshUrlMatch.Groups[2].Value}/{shortSshUrlMatch.Groups[3].Value}";
+                            }
+                        }
+                        // @note: This will round trip to ci-build as EngineSpecType.GitCommit
+                        return BuildEngineSpecification.ForGitCommitWithZips(
+                            repositoryUrl,
+                            engineDistribution.Source.Ref,
+                            engineDistribution.Source.ConsoleZips,
+                            isEngineBuild: true,
+                            windowsSharedGitCachePath: windowsSharedGitCachePath,
+                            macSharedGitCachePath: macSharedGitCachePath);
+                    }
+                    else
+                    {
+                        throw new NotSupportedException($"The EngineSpecType {Type} is not supported by the '{commandName}' command.");
+                    }
+                default:
+                    throw new NotSupportedException($"The EngineSpecType {Type} is not supported by the '{commandName}' command.");
+            }
+        }
     }
 }
diff --git a/UET/uet/Commands/Test/TestCommand.cs b/UET/uet/Commands/Test/TestCommand.cs
index 7bf4ec60..cb3d831e 100644
--- a/UET/uet/Commands/Test/TestCommand.cs
+++ b/UET/uet/Commands/Test/TestCommand.cs
@@ -143,27 +143,7 @@ public async Task<int> ExecuteAsync(InvocationContext context)
                 _logger.LogInformation($"--prefix:                      {prefix}");
                 _logger.LogInformation($"--name:                        {name}");
 
-                BuildEngineSpecification engineSpec;
-                switch (engine.Type)
-                {
-                    case EngineSpecType.UEFSPackageTag:
-                        engineSpec = BuildEngineSpecification.ForUEFSPackageTag(engine.UEFSPackageTag!);
-                        break;
-                    case EngineSpecType.SESNetworkShare:
-                        engineSpec = BuildEngineSpecification.ForSESNetworkShare(engine.SESNetworkShare!);
-                        break;
-                    case EngineSpecType.RemoteZfs:
-                        engineSpec = BuildEngineSpecification.ForRemoteZfs(engine.RemoteZfs!);
-                        break;
-                    case EngineSpecType.Version:
-                        engineSpec = BuildEngineSpecification.ForVersionWithPath(engine.Version!, engine.Path!);
-                        break;
-                    case EngineSpecType.Path:
-                        engineSpec = BuildEngineSpecification.ForAbsolutePath(engine.Path!);
-                        break;
-                    default:
-                        throw new NotSupportedException();
-                }
+                var engineSpec = engine.ToBuildEngineSpecification("test");
 
                 // @note: We always use the local executor for this test command.
                 var executor = _localBuildExecutorFactory.CreateExecutor();
diff --git a/UET/uet/Program.cs b/UET/uet/Program.cs
index 994103c0..70f6b7e3 100644
--- a/UET/uet/Program.cs
+++ b/UET/uet/Program.cs
@@ -9,6 +9,7 @@
 using System.Diagnostics;
 using System.Text.Json.Nodes;
 using System.Text.RegularExpressions;
+using UET.Commands.Android;
 using UET.Commands.AppleCert;
 using UET.Commands.Build;
 using UET.Commands.CMake;
@@ -47,6 +48,7 @@
 rootCommand.AddCommand(UefsCommand.CreateUefsCommand());
 rootCommand.AddCommand(TransferCommand.CreateTransferCommand());
 rootCommand.AddCommand(AppleCertCommand.CreateAppleCertCommand());
+rootCommand.AddCommand(AndroidCommand.CreateAndroidCommand());
 rootCommand.AddCommand(CMakeCommand.CreateCMakeCommand());
 rootCommand.AddCommand(InternalCommand.CreateInternalCommand(globalCommands));