From 15482093bd0cde101723600da40d231cdd5056d9 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 8 Jun 2026 09:10:59 -0500 Subject: [PATCH] [Xamarin.AndroidTools] Remove dead VS Mac / legacy code Context: after the `android-platform-support` repo was inlined here (see c22cdcf30), `src/Xamarin.AndroidTools/` carries a large amount of code that was only used by old VS Mac / installer / debugger-host workflows that are no longer part of the .NET for Android product. None of these types have callers anywhere in this repo, in `external/xamarin-android-tools/`, or in `external/Java.Interop/`. Removed (13 files): * `PublicationUtilities/PublishAndroidApplication.cs` * `PublicationUtilities/PackageSigningTasks.cs` * `PublicationUtilities/KeyManagement.cs` * `PublicationUtilities/KeystoreEntry.cs` * `Sessions/AndroidDeploySession.cs` * `Sessions/AndroidConnectCommandSession.cs` * `Sessions/AndroidCommandSession.cs` * `Sessions/AndroidDeploymentException.cs` * `Devices/AndroidPackageList.cs` * `Devices/AndroidPackageListExtensions.cs` * `Debugging/MonoDroidProcessMonitor.cs` * `PlatformPackage.cs` * `IProgressNotifier.cs` * `AndroidSigningOptions.cs` Moved (still needed by `ProcessUtils.cs`): * `PublicationUtilities/AndroidSdkToolException.cs` -> `AndroidSdkToolException.cs` (namespace unchanged) Pruned from `Devices/AndroidDeviceExtensions.cs` (the methods only referenced the just-deleted types and had no in-tree callers): * `StartActivityWithCommandSession` (used `AndroidCommandSession`) * `GetDeploySession`, `GetPackagesAsync`, `GetPackages` (used `AndroidDeploySession` / `IProgressNotifier`) * `InstallSharedRuntime*` and `InstallSharedPlatform*` overloads that took `IProgressNotifier` * `GetPackageRemotePath`/`GetPackageRemotePathAsync` (used `AndroidDeploymentException`) * `GetFastDevRemotePath`/`GetFastDevRemotePathAsync` and the inner `FastDevRemotePathInfo` class (only consumer was `GetPackageRemotePathAsync`) Kept methods in `AndroidDeviceExtensions.cs` are the ones still used by `Xamarin.Android.Build.Debugging.Tasks` (`EnsureProperties`, `KillProcessAndWaitForExit`, `PushAndInstallPackageAsync`) and by `DebuggingExtensions` (`SetFastDevPropertyFile`, `GetProcessIDAsync`, `SetDebugPropertiesAsync`). Also cleaned up: * `Properties/Resources.resx` (English source): removed seven `CreateKeyError_*` strings that only `KeyManagement.cs` used. * `Properties/Resources.Designer.cs`: removed the seven matching properties. * `MonoDroidSdk.cs`: two `[Obsolete]` messages referenced the now-deleted `PlatformPackage`; changed to `"Do not use."`. Non-English `Resources.*.resx` and `Localize/loc/**/*.lcl` are left alone per repo policy (auto-regenerated by OneLocBuild). Build verified locally: * `src/Xamarin.AndroidTools/Xamarin.AndroidTools.csproj` * `src/Mono.AndroidTools/Mono.AndroidTools.csproj` * `src/Xamarin.Android.Build.Debugging.Tasks/Xamarin.Android.Build.Debugging.Tasks.csproj` * `src/Xamarin.Installer.AndroidSDK/Xamarin.Installer.AndroidSDK.csproj` Net change: -4225 / +2 lines across 19 files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AndroidSdkToolException.cs | 0 .../AndroidSigningOptions.cs | 55 - .../Debugging/MonoDroidProcessMonitor.cs | 375 ------ .../Devices/AndroidDeviceExtensions.cs | 352 ------ .../Devices/AndroidPackageList.cs | 93 -- .../Devices/AndroidPackageListExtensions.cs | 101 -- src/Xamarin.AndroidTools/IProgressNotifier.cs | 24 - src/Xamarin.AndroidTools/MonoDroidSdk.cs | 4 +- src/Xamarin.AndroidTools/PlatformPackage.cs | 378 ------ .../Properties/Resources.Designer.cs | 63 - .../Properties/Resources.resx | 21 - .../PublicationUtilities/KeyManagement.cs | 558 --------- .../PublicationUtilities/KeystoreEntry.cs | 32 - .../PackageSigningTasks.cs | 462 -------- .../PublishAndroidApplication.cs | 95 -- .../Sessions/AndroidCommandSession.cs | 320 ----- .../Sessions/AndroidConnectCommandSession.cs | 65 - .../Sessions/AndroidDeploySession.cs | 1054 ----------------- .../Sessions/AndroidDeploymentException.cs | 175 --- 19 files changed, 2 insertions(+), 4225 deletions(-) rename src/Xamarin.AndroidTools/{PublicationUtilities => }/AndroidSdkToolException.cs (100%) delete mode 100644 src/Xamarin.AndroidTools/AndroidSigningOptions.cs delete mode 100644 src/Xamarin.AndroidTools/Debugging/MonoDroidProcessMonitor.cs delete mode 100644 src/Xamarin.AndroidTools/Devices/AndroidPackageList.cs delete mode 100644 src/Xamarin.AndroidTools/Devices/AndroidPackageListExtensions.cs delete mode 100644 src/Xamarin.AndroidTools/IProgressNotifier.cs delete mode 100644 src/Xamarin.AndroidTools/PlatformPackage.cs delete mode 100644 src/Xamarin.AndroidTools/PublicationUtilities/KeyManagement.cs delete mode 100644 src/Xamarin.AndroidTools/PublicationUtilities/KeystoreEntry.cs delete mode 100644 src/Xamarin.AndroidTools/PublicationUtilities/PackageSigningTasks.cs delete mode 100644 src/Xamarin.AndroidTools/PublicationUtilities/PublishAndroidApplication.cs delete mode 100644 src/Xamarin.AndroidTools/Sessions/AndroidCommandSession.cs delete mode 100644 src/Xamarin.AndroidTools/Sessions/AndroidConnectCommandSession.cs delete mode 100644 src/Xamarin.AndroidTools/Sessions/AndroidDeploySession.cs delete mode 100644 src/Xamarin.AndroidTools/Sessions/AndroidDeploymentException.cs diff --git a/src/Xamarin.AndroidTools/PublicationUtilities/AndroidSdkToolException.cs b/src/Xamarin.AndroidTools/AndroidSdkToolException.cs similarity index 100% rename from src/Xamarin.AndroidTools/PublicationUtilities/AndroidSdkToolException.cs rename to src/Xamarin.AndroidTools/AndroidSdkToolException.cs diff --git a/src/Xamarin.AndroidTools/AndroidSigningOptions.cs b/src/Xamarin.AndroidTools/AndroidSigningOptions.cs deleted file mode 100644 index fff5d242968..00000000000 --- a/src/Xamarin.AndroidTools/AndroidSigningOptions.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// AndroidSigningOptions.cs -// -// Author: -// Michael Hutchinson -// -// Copyright (c) 2010-2011 Novell, Inc. -// Copyright (c) 2011 Xamarin Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; - -namespace Xamarin.AndroidTools -{ - public enum PackageSigningAlgorithm - { - Unsupported, - RSA, - DSA, - SHA256withRSA - } - - public class AndroidSigningOptions - { - public AndroidSigningOptions () - { - SigningAlgorithm = PackageSigningAlgorithm.RSA; - } - - public string KeyStore { get; set; } - public string KeyAlias { get; set; } - public string KeyPass { get; set; } - public string StorePass { get; set; } - public string TsaUrl { get; set; } - public int? MinSdkVersion { get; set; } - public PackageSigningAlgorithm SigningAlgorithm { get; set; } - } -} \ No newline at end of file diff --git a/src/Xamarin.AndroidTools/Debugging/MonoDroidProcessMonitor.cs b/src/Xamarin.AndroidTools/Debugging/MonoDroidProcessMonitor.cs deleted file mode 100644 index a18cd104207..00000000000 --- a/src/Xamarin.AndroidTools/Debugging/MonoDroidProcessMonitor.cs +++ /dev/null @@ -1,375 +0,0 @@ -// -// MonoDroidProcessMonitor.cs -// -// Author: -// Greg Munn -// -// Copyright (c) 2015 Xamarin Inc -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using Mono.AndroidTools; - -namespace Xamarin.AndroidTools.Debugging -{ - /// - /// Monitors a running MonoDroid process during a debugging session - /// - /// something will launch the process, - /// this will monitor the device for the processes that match the given package name - /// once we find a process that matches we watch for that process to exit - /// - /// - onCompleted => end debugger session - /// - /// monitors logcat and outputs stdout and error messages - /// can optionally kill the process when disposed - /// - /// how to test? - /// - launch task - /// - get process task - /// - get logcat task - /// - kill process - /// - need to mock / augment device, maybe make the methods virtual on device - /// - or have an IDevice and we can pass a mock in instead - /// - /// - /// - public sealed class MonoDroidProcessMonitor : IDisposable - { - const int UNASSIGNED_PID = -1; - public static int RefreshPidInterval = 1000; - - // Common system tags that we may want to ignore - readonly static string [] excludedLogTags = { - "dalvikvm", - "ActivityThread", - "mkestner", - "MonoDroid-Debugger" - }; - - readonly ManualResetEvent endHandle = new ManualResetEvent (false); - readonly object lockObj = new object (); - readonly CancellationTokenSource cancellationSource; - readonly IAndroidDevice device; - readonly string packageName; - readonly Action onStdOut; - readonly Action onStdError; - readonly Func killProcessOnExit; - readonly Action onCompleted; - - /// - /// Func to return a task that will reset the debugger timeout, we use this to extend the debugger timeout to give the user - /// time to launch their app in the case where there is no launchable activity - /// - readonly Func resetTimeout; - - Task getPidTask; - Task loggingTask; - volatile int pid = UNASSIGNED_PID; - - public MonoDroidProcessMonitor (IAndroidDevice device, string packageName, Action stdout, Action stderr, - CancellationTokenSource cancellationSource, Func killProcessOnExit, Func resetTimeout, Action onCompleted) - { - if (device == null) - throw new ArgumentNullException ("device"); - if (packageName == null) - throw new ArgumentNullException ("packageName"); - if (stdout == null) - throw new ArgumentNullException ("stdout"); - if (stderr == null) - throw new ArgumentNullException ("stderr"); - if (cancellationSource == null) - throw new ArgumentNullException ("cancellationSource"); - - this.cancellationSource = cancellationSource; - this.device = device; - this.packageName = packageName; - this.onStdOut = stdout; - this.onStdError = stderr; - this.killProcessOnExit = killProcessOnExit; - this.resetTimeout = resetTimeout; - this.onCompleted = onCompleted; - } - - public bool IsStarted { get; private set; } - - public bool IsCompleted { get; private set; } - - /// - /// Starts the monitoring process. If processLaunchTask is non-null, waits for the launch task to complete before monitoring - /// and completes the monitor if the launch failed - /// - public void Start (Task processLaunchTask = null) - { - if (this.IsStarted) - throw new InvalidOperationException ("Already Started"); - - this.IsStarted = true; - this.StartMonitoring (processLaunchTask); - } - - public void Dispose () - { - if (!cancellationSource.IsCancellationRequested) - cancellationSource.Cancel (); - cancellationSource.Dispose (); - } - - public void Cancel () - { - lock (lockObj) { - if (IsCompleted) - return; - - // Make sure our tracking operations are finished first - cancellationSource.Cancel (); - - // Try to kill the activity if we were able to actually get its pid - if (pid != UNASSIGNED_PID && killProcessOnExit != null) { - var killTask = killProcessOnExit (device, packageName); - killTask.ContinueWith (t => { - if (t.IsFaulted) - AndroidLogger.LogError ("Failed to kill application", t.Exception.Flatten ().InnerException); - }); - } - } - - SetCompleted (); - } - - /// - /// Waits for the monitor to be completed, usually when the process has exited - /// - public void WaitForCompleted () - { - lock (lockObj) { - if (IsCompleted) - return; - } - - endHandle.WaitOne (); - } - - /// - /// Waits for the monitor to be completed, usually when the process has exited - /// - public void WaitForCompleted (int timeout) - { - lock (lockObj) { - if (IsCompleted) - return; - } - - endHandle.WaitOne (timeout); - } - - /// - /// Starts the monitoring process. If processLaunchTask is null or completed, starts tracking the process on the device - /// - void StartMonitoring (Task processLaunchTask) - { - if (processLaunchTask == null) { - StartTrackingProcess (); - return; - } - - processLaunchTask.ContinueWith (t => { - if (t.IsFaulted) { - var ex = t.Exception.Flatten ().InnerException; - onStdOut (string.Format ("\n Failed to launch app: {0}\n", ex.Message)); - AndroidLogger.LogError ("Failed to launch app", ex); - SetCompleted (); - } else if (cancellationSource.IsCancellationRequested || t.IsCanceled) { - SetCompleted (); - } else { - StartTrackingProcess (); - } - }, cancellationSource.Token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - /// - /// Starts tracking the process on the device. We determine the process to track by the package name. - /// - void StartTrackingProcess () - { - if (cancellationSource.IsCancellationRequested) - return; - - try { - getPidTask = device.GetProcessId (packageName, cancellationSource.Token); - if (getPidTask == null) - throw new InvalidOperationException ("device.GetProcessId returned null, cannot track process"); - } catch (Exception ex) { - AndroidLogger.LogError ("Failure to get ProcessId task", ex); - SetCompleted (); - return; - } - - getPidTask.ContinueWith (RefreshPid); - } - - /// - /// Refreshs the pid of the process on the device, if the process has not yet started calls StartTrackingProcess again after a timeout. - /// - void RefreshPid (Task processIdTask) - { - if (cancellationSource.IsCancellationRequested) - return; - - if (processIdTask.IsFaulted) { - AndroidLogger.LogError ("Failed to get PID", processIdTask.Exception.Flatten ().InnerException); - SetCompleted (); - return; - } - if (processIdTask.IsCanceled) { - return; - } - - int resultPid = processIdTask.Result; - if (pid == UNASSIGNED_PID) { - // Ignore if the activity is still starting, and thus doesn't show up in 'ps' - if (resultPid > 0) { - pid = resultPid; - StartLogTracking (); // track log *after* getting the pid - } - } else { - if (resultPid == 0 || pid != resultPid) { - SetCompleted (); - return; - } - } - - //carry on polling the PID, so we can detect when the app exits - StartTrackingAfterTimeout (); - } - - static Task Delay(double milliseconds, CancellationToken token) - { - var tcs = new TaskCompletionSource(); - System.Timers.Timer timer = new System.Timers.Timer(); - timer.Elapsed+= (obj, args) => tcs.TrySetResult (true); - timer.Interval = milliseconds; - timer.AutoReset = false; - timer.Start(); - return tcs.Task; - } - - void StartTrackingAfterTimeout () - { - if (cancellationSource.IsCancellationRequested) - return; - - // TODO: use a Task.Delay once we can use .NET 4.5 - var delay = Delay (RefreshPidInterval, cancellationSource.Token); - delay.ContinueWith ((d) => { - StartTrackingProcess (); - - // if we are debugging an app that has no start up activity, like a watchface app, then - // periodically reset the debug timeout so that the user has time to start up the watchface - if (pid == UNASSIGNED_PID) { - ResetDebugerTimeout (); - } - }); - } - - /// - /// Starts monitoring logcat and outputs stdout and stderror - /// - void StartLogTracking () - { - try { - loggingTask = device.GetLogCat (ProcessLogLine, cancellationSource.Token, excludedLogTags); - if (loggingTask == null) - throw new InvalidOperationException ("device.GetLogCat returned null, cannot monitor logcat"); - } catch (Exception ex) { - AndroidLogger.LogError ("Failure to get LogCat task", ex); - SetCompleted (); - return; - } - - loggingTask.ContinueWith (t => { - if (t.IsFaulted) { - AndroidLogger.LogError ("Logcat ended unexpectedly", t.Exception.Flatten ().InnerException); - SetCompleted (); - } - }, cancellationSource.Token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - /// - /// Resets the debugger output, if the process has not started then bump the debugger timeout to give additional time for it to start - /// This is used when the user has to manually start up the process (eg. watchface apps) - /// - void ResetDebugerTimeout () - { - if (cancellationSource.IsCancellationRequested) - return; - - if (resetTimeout != null) { - var timeoutTask = resetTimeout (); - try { - timeoutTask.Wait (cancellationSource.Token); - } - catch (AggregateException ex) { - if (ex.InnerException is OperationCanceledException) { - // expected, nothing to do - } else - throw; - } - catch (OperationCanceledException) { - // expected, nothing to do - } - catch (Exception ex) { - AndroidLogger.LogError ("Failed to reset timeout for debugger", ex); - } - } - } - - /// - /// Processes a line from logcat - /// - void ProcessLogLine (AndroidLogCatEntry entry) - { - // Disable the time check for now, as we need to use device-only dates - // We may implement a date retrieval later if needed - //if (pid != this.pid || time < startTime) - if (entry.Pid != this.pid) - return; - - switch (entry.Tag) { - case "mono-stdout": - case "stdout": - onStdOut (entry.Message + "\n"); - break; - case "mono-stderr": - case "stderr": - onStdError (entry.Message + "\n"); - break; - default: - onStdOut (string.Format ("[{0}] {1}\n", entry.Tag, entry.Message)); - break; - } - } - - void SetCompleted () - { - lock (lockObj) { - if (IsCompleted) - return; - - endHandle.Set (); - IsCompleted = true; - } - - try { - if (onCompleted != null) - onCompleted (); - - cancellationSource.Cancel (); - } catch (Exception ex) { - AndroidLogger.LogError ("Unhandled error completing MonoDroidProcess", ex); - } - } - } -} \ No newline at end of file diff --git a/src/Xamarin.AndroidTools/Devices/AndroidDeviceExtensions.cs b/src/Xamarin.AndroidTools/Devices/AndroidDeviceExtensions.cs index 9a4118e3505..fd8c99298d1 100644 --- a/src/Xamarin.AndroidTools/Devices/AndroidDeviceExtensions.cs +++ b/src/Xamarin.AndroidTools/Devices/AndroidDeviceExtensions.cs @@ -359,93 +359,6 @@ static Task TaskDelay (int delayMilliseconds) return tcs.Task; } - static public Task StartActivityWithCommandSession (this AndroidDevice device, string package, string activity, IPAddress address, int port, CancellationToken token) - { - var tcs = new TaskCompletionSource (); - var connection = new AndroidConnectCommandSession(address, port); - - var launchTask = AdbServer.Default.ForwardPort (device, port, port, token) - .ContinueWith (t => { - t.Wait (); - return device.SetProperty ("debug.mono.connect", "port=" + port); - }, token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Unwrap () - .ContinueWith (t => { - t.Wait (); - return device.KillProcessIfRunningAndWaitForExit (package, token); - }, token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Unwrap () - .ContinueWith (t => { - t.Wait (); - return device.StartActivity ( - "android.intent.action.MAIN", new [] { "android.intent.category.LAUNCHER" }, - package, activity, false, token); - }, token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Unwrap (); - - // This just initializes the socket, but doesn't actually do any work yet - Action onConnect = null; - - Action cleanup = () => { - try { - connection.Dispose(); - } catch (Exception ex) { - AndroidLogger.LogError ("Failed to clean up Android command session", ex); - } - }; - - int retries = 10; - onConnect = t => { - //faulted, and out of retries or not an IOException - if (t.IsFaulted && (!(t.Exception.Flatten().InnerException is IOException) || retries < 0)) { - tcs.TrySetException(t.Exception); - cleanup(); - return; - } - //cancelled - if (t.IsCanceled || token.IsCancellationRequested) { - tcs.TrySetCanceled(); - cleanup(); - return; - } - //success - if (t.IsCompleted && !t.IsFaulted) { - tcs.TrySetResult (connection); - device.SetProperty ("debug.mono.connect", "", token).Wait (); - return; - } - - cleanup(); - - //retry after a small delay - retries--; - TaskDelay(200).ContinueWith (td => { - if (td.IsFaulted) { - tcs.TrySetException (td.Exception); - } else if (td.IsCanceled || token.IsCancellationRequested) { - tcs.TrySetCanceled (); - } else { - try { - connection = new AndroidConnectCommandSession(address, port); - Task.Factory.FromAsync (connection.BeginHandshake (), connection.EndHandshake).ContinueWith (onConnect); - } catch (Exception ex) { - tcs.TrySetException (ex); - } - } - }); - }; - - launchTask.ContinueWith (t => { - try { - t.Wait(); - token.Register (cleanup); - Task.Factory.FromAsync(connection.BeginHandshake (), connection.EndHandshake) - .ContinueWith (onConnect); - } catch (Exception ex) { - tcs.TrySetException (ex); - } - }); - - return tcs.Task; - } - public static async Task InstallSharedRuntimeAsync (this AndroidDevice device, string runtimeFile, AdbProgressReporter progress, CancellationToken token) { // See if we were asked to cancel @@ -499,235 +412,8 @@ public static void InstallSharedRuntime (this AndroidDevice device, string runti } } - public static async Task InstallSharedPlatformAsync (this AndroidDevice device, string platformFile, int packageApiLevel, AdbProgressReporter progress, CancellationToken token) - { - // See if we were asked to cancel - token.ThrowIfCancellationRequested (); - - try { - await device.PushAndInstallPackageAsync (new PushAndInstallCommand () { - ApkFile = platformFile, - PackageName = GetPlatformPackageName (packageApiLevel), - ReInstall = false, - NotifyProgress = progress, - }, token); - } catch (Exception ex) { - var aex = ex as AggregateException; - if (aex != null) { - ex = aex.Flatten ().InnerException; - } - - // If the runtime already exists, ignore the error - // Sometimes android doesn't report it's installed when it is :/ - if (ex is PackageAlreadyExistsException) - return; - - throw; - } - } - - [Obsolete ("Use InstallSharedPlatformAsync")] - public static void InstallSharedPlatform (this AndroidDevice device, string platformFile, int packageApiLevel, AdbProgressReporter progress, CancellationToken token) - { - // See if we were asked to cancel - token.ThrowIfCancellationRequested (); - - try { - device.PushAndInstallPackageAsync (new PushAndInstallCommand () { - ApkFile = platformFile, - PackageName = GetPlatformPackageName (packageApiLevel), - ReInstall = false, - NotifyProgress = progress, - }, token).Wait (); - } catch (Exception ex) { - var aex = ex as AggregateException; - if (aex != null) { - ex = aex.Flatten ().InnerException; - } - - // If the runtime already exists, ignore the error - // Sometimes android doesn't report it's installed when it is :/ - if (ex is PackageAlreadyExistsException) - return; - - throw; - } - } - - public static AndroidDeploySession GetDeploySession (this AndroidDevice device, IList packages = null) - { - return new AndroidDeploySession (device, packages); - } - - public static async Task> GetPackagesAsync (this AndroidDevice device, int packageApiLevel, string packageName, bool provideFullDebugRuntime, CancellationToken cancellationToken, IProgressNotifier progressReporter) - { - // See what API level the device supports - await device.EnsureProperties (cancellationToken).ConfigureAwait(false); - - progressReporter.BeginStep ("Detecting installed packages"); - progressReporter.ReportMessage ("Detecting installed packages..."); - try { - string versions = await GetPackageVersionsAsync (device, packageApiLevel, packageName, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace (versions)) - return GetInstalledPackages (versions); - - var p = await GetInstalledPackagesFromDatabaseAsync (device, true, cancellationToken).ConfigureAwait(false); - if (p != null) - return p; - - return await GetInstalledPackagesFromDatabaseAsync (device, false, cancellationToken).ConfigureAwait(false); - } finally { - progressReporter.EndStep (""); - } - } - - [Obsolete ("Use GetPackagesAsync")] - public static IList GetPackages (this AndroidDevice device, int packageApiLevel, string packageName, bool provideFullDebugRuntime, CancellationToken cancellationToken, IProgressNotifier progressReporter) - { - // See what API level the device supports - device.EnsureProperties (cancellationToken).Wait (); - - progressReporter.BeginStep ("Detecting installed packages"); - progressReporter.ReportMessage ("Detecting installed packages..."); - try { - string versions = GetPackageVersions (device, packageApiLevel, packageName, cancellationToken); - if (!string.IsNullOrWhiteSpace (versions)) - return GetInstalledPackages (versions); - - var p = GetInstalledPackagesFromDatabase (device, true, cancellationToken); - if (p != null) - return p; - - return GetInstalledPackagesFromDatabase (device, false, cancellationToken); - } finally { - progressReporter.EndStep (""); - } - } - - [Obsolete("Use GetInstalledPackagesFromDatabaseAsync")] - static IList GetInstalledPackagesFromDatabase (AndroidDevice device, bool requireVersions, CancellationToken cancellationToken) - { - return device.GetPackages (requireVersions, cancellationToken).Result; - } - - static Task> GetInstalledPackagesFromDatabaseAsync (AndroidDevice device, bool requireVersions, CancellationToken cancellationToken) - { - return device.GetPackages (requireVersions, cancellationToken); - } - - static string GetPlatformPackageName (int packageApiLevel) - { - return string.Format (AndroidPackageListExtensions.platformName, packageApiLevel); - } - - [Obsolete("Use GetPackageVersionsAsync")] - static string GetPackageVersions (AndroidDevice device, int packageApiLevel, string packageName, CancellationToken cancellationToken) - { - var action = "mono.android.intent.action.PACKAGE_VERSIONS"; - var platform = GetPlatformPackageName (packageApiLevel); - PlatformPackage.GetPlatformPackageVersion (packageApiLevel, ref platform); - var packages = string.Join (",", new[]{ - AndroidPackageListExtensions.runtimeName, - platform, - packageName, - }); - var extras = new Dictionary { - { "packages", packages }, - }; - return device.Broadcast (action, - null, - extras, - "Mono.Android.DebugRuntime/com.xamarin.mono.android.PackageVersions", - cancellationToken).Result; - } - - async static Task GetPackageVersionsAsync(AndroidDevice device, int packageApiLevel, string packageName, CancellationToken cancellationToken) - { - var action = "mono.android.intent.action.PACKAGE_VERSIONS"; - var platform = GetPlatformPackageName(packageApiLevel); - var packages = string.Join(",", new[]{ - AndroidPackageListExtensions.runtimeName, - platform, - packageName, - }); - var extras = new Dictionary { - { "packages", packages }, - }; - return await device.Broadcast(action, - null, - extras, - "Mono.Android.DebugRuntime/com.xamarin.mono.android.PackageVersions", - cancellationToken).ConfigureAwait(false); - } - - static IList GetInstalledPackages (string packageVersions) - { - return packageVersions.Split (new[]{','}, StringSplitOptions.RemoveEmptyEntries).Select (v => { - string[] p = v.Split ('='); - return new AndroidInstalledPackage (p [0], null, int.Parse (p [1])); - }).ToList (); - } - - internal static async Task InstallSharedRuntimeAsync (this AndroidDevice device, bool provideFullDebugRuntime, CancellationToken cancellationToken, IProgressNotifier progressReporter) - { - var arch = device.Properties.ProductCpuAbi; - progressReporter.ReportMessage ("Target device is " + arch + "."); - - var runtime_file = MonoDroidSdk.GetSharedRuntimePackage (provideFullDebugRuntime, arch); - var runtime_desc = runtime_file.EndsWith ("-debug.apk", StringComparison.Ordinal) ? "debug" : arch; - - // Install the runtime - var text = string.Format ("Installing the Mono shared runtime ({0} - {1})...", runtime_desc, MonoDroidSdk.SharedRuntimeVersion); - progressReporter.ReportMessage (text); - await device.InstallSharedRuntimeAsync (runtime_file, progressReporter.ReportProgress, cancellationToken); - } - - [Obsolete ("Use InstallSharedRuntimeAsync")] - internal static void InstallSharedRuntime (this AndroidDevice device, bool provideFullDebugRuntime, CancellationToken cancellationToken, IProgressNotifier progressReporter) - { - var arch = device.Properties.ProductCpuAbi; - progressReporter.ReportMessage ("Target device is " + arch + "."); - - var runtime_file = MonoDroidSdk.GetSharedRuntimePackage (provideFullDebugRuntime, arch); - var runtime_desc = runtime_file.EndsWith ("-debug.apk", StringComparison.Ordinal) ? "debug" : arch; - - // Install the runtime - var text = string.Format ("Installing the Mono shared runtime ({0} - {1})...", runtime_desc, MonoDroidSdk.SharedRuntimeVersion); - progressReporter.ReportMessage (text); - device.InstallSharedRuntime (runtime_file, progressReporter.ReportProgress, cancellationToken); - } - const string PackageInstallLocationFormat = "data/{0}/files/.__override__"; - [Obsolete ("Use GetPackageRemotePathAsync")] - public static string GetPackageRemotePath (this AndroidDevice device, string packageName, CancellationToken cancellationToken) - { - var x = device.RunShellCommand (cancellationToken, "pm", "path", packageName).Result; - - string[] packagePathInfo = x.Split (':'); - if (packagePathInfo.Length <= 1) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InternalError, - new InvalidOperationException ( - string.Format ("Could not determine the installation path for package {0}. " + - "`adb shell pm path {0}` returned '{1}'.", packageName, x))); - } - return packagePathInfo [1]; - } - - public static async Task GetPackageRemotePathAsync (this AndroidDevice device, string packageName, CancellationToken cancellationToken) - { - var x = await device.RunShellCommand (cancellationToken, "pm", "path", packageName); - - string[] packagePathInfo = x.Split (':'); - if (packagePathInfo.Length <= 1) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InternalError, - new InvalidOperationException ( - string.Format ("Could not determine the installation path for package {0}. " + - "`adb shell pm path {0}` returned '{1}'.", packageName, x))); - } - return packagePathInfo [1]; - } - public static async Task GetFastDevRemotePathInternalAsync (this AndroidDevice device, string packageName, CancellationToken cancellationToken) { var internalPath = await device.RunShellCommand (cancellationToken, "run-as", packageName, "pwd"); @@ -775,44 +461,6 @@ public static async Task GetFastDevRemotePathExternalAsync (this Android } return string.Format ("{0}/Android/{1}", esd, string.Format (PackageInstallLocationFormat, packageName)); } - - public class FastDevRemotePathInfo - { - public FastDevRemotePathInfo (string fastDevRemotePath, string packageRemotePath, bool external) - { - this.Root = fastDevRemotePath; - PackageRemotePath = packageRemotePath; - this.IsExternal = external; - } - - public string Root { get; set; } - public string PackageRemotePath { get; set; } - public bool IsExternal { get; set; } - } - - [Obsolete ("Use GetFastDevRemotePathAsync()")] - public static string GetFastDevRemotePath (this AndroidDevice device, string packageName, CancellationToken cancellationToken, out string packageRemotePath, out bool external) - { - var ret = GetFastDevRemotePathAsync (device, packageName, cancellationToken).Result; - packageRemotePath = ret.PackageRemotePath; - external = ret.IsExternal; - return ret.Root; - } - - public static async Task GetFastDevRemotePathAsync (this AndroidDevice device, string packageName, CancellationToken cancellationToken) - { - string packageRemotePath = await GetPackageRemotePathAsync (device, packageName, cancellationToken); - bool external = !packageRemotePath.StartsWith ("/data", StringComparison.Ordinal); - - var root = "/data/"; - if (external) { - var ex = await device.Broadcast ("mono.android.intent.action.EXTERNAL_STORAGE_DIRECTORY", null, cancellationToken); - if (!string.IsNullOrEmpty (ex)) - root = ex + "/Android/"; - } - - return new FastDevRemotePathInfo (root + string.Format (PackageInstallLocationFormat, packageName), packageRemotePath, external); - } } public class PushAndInstallCommand { diff --git a/src/Xamarin.AndroidTools/Devices/AndroidPackageList.cs b/src/Xamarin.AndroidTools/Devices/AndroidPackageList.cs deleted file mode 100644 index a2c4febdf16..00000000000 --- a/src/Xamarin.AndroidTools/Devices/AndroidPackageList.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// AndroidPackageList.cs -// -// Authors: -// Jonathan Pobst -// -// Copyright 2011 Xamarin Inc. All rights reserved. -// - -using System; -using System.Collections.Generic; -using System.Linq; -using Mono.AndroidTools; - -namespace Xamarin.AndroidTools -{ - [Obsolete ("Use AndroidPackageListExtensions")] - public class AndroidPackageList - { - string runtimeName = "Mono.Android.DebugRuntime"; - string oldRuntimeName = "com.novell.monodroid.runtimeservice"; - string platformName = "Mono.Android.Platform.ApiLevel_{0}"; - - public List Packages { get; private set; } - - public AndroidPackageList (List packages) - { - Packages = packages; - } - - public bool IsCurrentRuntimeInstalled (int runtimeVersion) - { - return Packages.Any (p => p.Name == runtimeName && p.Version == runtimeVersion); - } - - public bool IsUnknownRuntimeInstalled () - { - return Packages.Any (p => p.Name == runtimeName && p.Version == int.MaxValue); - } - - public List GetOldRuntimes (int runtimeVersion) - { - return Packages.Where (p => (p.Name == runtimeName && p.Version != runtimeVersion) || p.Name == oldRuntimeName).ToList (); - } - - public bool IsCurrentPlatformInstalled (string apiLevel, int runtimeVersion) - { - string name = string.Format (platformName, apiLevel); - - return Packages.Any (p => p.Name == name && p.Version == runtimeVersion); - } - - public bool IsUnknownPlatformInstalled (string apiLevel) - { - string name = string.Format (platformName, apiLevel); - - return Packages.Any (p => p.Name == name && p.Version == int.MaxValue); - } - - public bool AreCurrentRuntimeAndPlatformInstalled (string apiLevel, int runtimeVersion) - { - return IsCurrentRuntimeInstalled (runtimeVersion) && IsCurrentPlatformInstalled (apiLevel, runtimeVersion); - } - - // Hopefully they don't have multiple old - // platforms installed, but just in case... - public List GetOldPlatforms (string apiLevel, int runtimeVersion) - { - string name = string.Format (platformName, apiLevel); - - return Packages.Where (p => p.Name == name && p.Version != runtimeVersion).ToList (); - } - - public List GetOldRuntimesAndPlatforms (string apiLevel, int runtimeVersion) - { - var runtimes = GetOldRuntimes (runtimeVersion); - - runtimes.AddRange (GetOldPlatforms (apiLevel, runtimeVersion)); - - return runtimes; - } - - public bool ContainsPackage (string packageName) - { - return Packages.Any (p => p.Name == packageName); - } - - public AndroidInstalledPackage GetPackage (string packageName) - { - return Packages.Where (x => x.Name == packageName).SingleOrDefault (); - } - } -} \ No newline at end of file diff --git a/src/Xamarin.AndroidTools/Devices/AndroidPackageListExtensions.cs b/src/Xamarin.AndroidTools/Devices/AndroidPackageListExtensions.cs deleted file mode 100644 index c95a70c7ded..00000000000 --- a/src/Xamarin.AndroidTools/Devices/AndroidPackageListExtensions.cs +++ /dev/null @@ -1,101 +0,0 @@ -// -// AndroidPackageListExtensions.cs -// -// Author: -// Michael Hutchinson -// -// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System.Collections.Generic; -using System.Linq; -using Mono.AndroidTools; - -namespace Xamarin.AndroidTools -{ - public static class AndroidPackageListExtensions - { - internal static readonly string runtimeName = "Mono.Android.DebugRuntime"; - internal static readonly string oldRuntimeName = "com.novell.monodroid.runtimeservice"; - internal static readonly string platformName = "Mono.Android.Platform.ApiLevel_{0}"; - - public static bool IsCurrentRuntimeInstalled (this IList packages) - { - var version = MonoDroidSdk.SharedRuntimeVersion; - return packages.Any (p => p.Name == runtimeName && p.Version == version); - } - - public static bool IsUnknownRuntimeInstalled (this IList packages) - { - return packages.Any (p => p.Name == runtimeName && p.Version == int.MaxValue); - } - - public static IEnumerable GetOldRuntimes (this IList packages) - { - var version = MonoDroidSdk.SharedRuntimeVersion; - return packages.Where (p => (p.Name == runtimeName && p.Version != version) || p.Name == oldRuntimeName); - } - - public static bool IsCurrentPlatformInstalled (this IList packages, int apiLevel) - { - string name = string.Format (platformName, apiLevel); - var version = PlatformPackage.GetPlatformPackageVersion (apiLevel, ref name); - - return packages.Any (p => p.Name == name && p.Version == version); - } - - public static bool IsUnknownPlatformInstalled (this IList packages, int apiLevel) - { - string name = string.Format (platformName, apiLevel); - - return packages.Any (p => p.Name == name && p.Version == int.MaxValue); - } - - public static bool AreCurrentRuntimeAndPlatformInstalled (this IList packages, int apiLevel) - { - return packages.IsCurrentRuntimeInstalled () && packages.IsCurrentPlatformInstalled (apiLevel); - } - - // Hopefully they don't have multiple old - // platforms installed, but just in case... - public static List GetOldPlatforms (this IList packages, int apiLevel) - { - string name = string.Format (platformName, apiLevel); - int version = PlatformPackage.GetPlatformPackageVersion (apiLevel, ref name); - - return packages.Where (p => p.Name == name && p.Version != version).ToList (); - } - - public static IEnumerable GetOldRuntimesAndPlatforms (this IList packages, int apiLevel) - { - return packages.GetOldRuntimes ().Concat (packages.GetOldPlatforms (apiLevel)); - } - - public static bool ContainsPackage (this IList packages, string packageName) - { - return packages.Any (p => p.Name == packageName); - } - - public static AndroidInstalledPackage GetPackage (this IList packages, string packageName) - { - return packages.SingleOrDefault (x => x.Name == packageName); - } - } -} diff --git a/src/Xamarin.AndroidTools/IProgressNotifier.cs b/src/Xamarin.AndroidTools/IProgressNotifier.cs deleted file mode 100644 index 073fe6dfe26..00000000000 --- a/src/Xamarin.AndroidTools/IProgressNotifier.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// IProgressNotifier.cs -// -// Authors: -// Jonathan Pobst -// -// Copyright 2012 Xamarin Inc. All rights reserved. -// - -using System; - -namespace Xamarin.AndroidTools -{ - public interface IProgressNotifier - { - void BeginStep (string step); - void EndStep (string step); - - void ReportMessage (string message); - void ShowErrorDialog (string title, string message); - void ShowErrorDialog (string title, string message, Exception ex); - void ReportProgress (long copiedBytes, long totalBytes); - } -} diff --git a/src/Xamarin.AndroidTools/MonoDroidSdk.cs b/src/Xamarin.AndroidTools/MonoDroidSdk.cs index bd87a3c5972..0df8e490200 100644 --- a/src/Xamarin.AndroidTools/MonoDroidSdk.cs +++ b/src/Xamarin.AndroidTools/MonoDroidSdk.cs @@ -215,13 +215,13 @@ public static string MDocExe { get { return Path.Combine (RuntimePath, "mdoc.exe"); } } - [Obsolete ("Please use PlatformPackage.GetPlatformPackagePath")] + [Obsolete ("Do not use.")] public static string GetPlatformRuntimePackage (int apiLevel) { return Path.Combine (RuntimePath, "platforms", "android-" + apiLevel, "Mono.Android.Platform.apk"); } - [Obsolete ("Please use PlatformPackage.GetPlatformPackageVersion")] + [Obsolete ("Do not use.")] public static int GetPlatformRuntimePackageVersion (int apiLevel) { string manifest = Path.Combine (RuntimePath, "platforms", "android-" + apiLevel, "Mono.Android.Platform.xml"); diff --git a/src/Xamarin.AndroidTools/PlatformPackage.cs b/src/Xamarin.AndroidTools/PlatformPackage.cs deleted file mode 100644 index fbbdeb3c4fc..00000000000 --- a/src/Xamarin.AndroidTools/PlatformPackage.cs +++ /dev/null @@ -1,378 +0,0 @@ -// -// PlatformPackage.cs -// -// Author: -// Jonathan Pryor -// -// Copyright (c) 2014 Xamarin Inc -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.Linq; -using Xamarin.AndroidTools.PublicationUtilities; - -namespace Xamarin.AndroidTools { - - public static class PlatformPackage { - - [Obsolete ("Use GetPlatformPackageVersion(int, ref string)")] - public static int GetPlatformPackageVersion (int apiLevel) - { - string packageName = null; - return GetPlatformPackageVersion (apiLevel, ref packageName); - } - - public static int GetPlatformPackageVersion (int apiLevel, ref string packageName) - { - // If this throws an ArgumentNullException from Path.Combine(), it's because MonoDroidSdk.RuntimePath is null - // To fix, either: - // 1. Provide a `runtimePath` value to `MonoDroidSdk.Refresh()` before calling this method, or - // 2. Export the `$MONO_ANDROID_PATH` environment variable to an appropriate `runtimePath` value. - string manifest = Path.Combine (MonoDroidSdk.RuntimePath, "platforms", "android-" + apiLevel, "Mono.Android.Platform.xml"); - if (File.Exists (manifest)) - return MonoDroidSdkBase.GetManifestVersion (manifest); - - string frameworkVersion = MonoDroidSdk.GetFrameworkVersionForApiLevel (apiLevel.ToString ()); - - return GetVersionInfo (frameworkVersion); - } - - internal static Version ToVersion (string frameworkDir) - { - string version = Path.GetFileName (frameworkDir); - if (!version.StartsWith ("v", StringComparison.OrdinalIgnoreCase)) { - // wat? - return new Version (); - } - version = version.Substring (1); - Version v; - if (Version.TryParse (version, out v)) - return v; - return new Version (); - } - - static int GetVersionInfo (string frameworkVersion) - { - string bclDir = MonoDroidSdk.FrameworkPath; - string frameworksDir = Path.GetDirectoryName (bclDir); - - string platform = Path.Combine (frameworksDir, frameworkVersion, "Mono.Android.dll"); - var platformTime = File.GetLastWriteTimeUtc (platform); - var unixEpoch = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - return (int) (platformTime - unixEpoch).TotalSeconds; - } - - [Obsolete ("Use GetPlatformPackagePathAsync")] - public static string GetPlatformPackagePath (int apiLevel, string aaptPath, IProgressNotifier progressReporter, CancellationToken token) - { - var task = GetPlatformPackagePathAsync (apiLevel, aaptPath, progressReporter, token); - task.Wait (token); - return task.Result; - } - - public static async Task GetPlatformPackagePathAsync (int apiLevel, string aaptPath, IProgressNotifier progressReporter, CancellationToken token) - { - string path = Path.Combine (MonoDroidSdk.RuntimePath, "platforms", "android-" + apiLevel, "Mono.Android.Platform.apk"); - if (File.Exists (path)) - return path; - - string cacheDir = OS.GetXamarinAndroidCacheDir (); - string manifest = Path.Combine (cacheDir, string.Format ("Mono.Android.Platform.ApiLevel_{0}.xml", apiLevel)); - string apkName = string.Format ("Mono.Android.Platform.ApiLevel_{0}.apk", apiLevel); - - path = Path.Combine (cacheDir, apkName); - - string frameworkVersion = MonoDroidSdk.GetFrameworkVersionForApiLevel (apiLevel.ToString ()); - - int version = GetVersionInfo (frameworkVersion); - - if (File.Exists (manifest) && File.Exists (path)) { - int curVersion = MonoDroidSdkBase.GetManifestVersion (manifest); - if (version == curVersion) - return path; - } - - aaptPath = aaptPath ?? AndroidSdk.GetAaptPath (); - - if (aaptPath == null) - throw new ArgumentNullException ("aaptPath", "'aaptPath' is null and no pre-built Platform.apk exists!"); - if (!File.Exists (aaptPath)) - throw new ArgumentException ("Could not find `aapt` and no pre-built Platform.apk exists.", "aaptPath"); - - ReportBeginStep (progressReporter, "Creating " + path); - - string packageDir = Path.Combine (Path.GetTempPath (), Path.GetRandomFileName ()); - string resourceDir = Path.Combine (packageDir, "r"); - string tmanifest = Path.Combine (packageDir, "AndroidManifest.xml"); - string tpath = Path.Combine (packageDir, apkName); - - Directory.CreateDirectory (packageDir); - try { - CopyAssemblies (frameworkVersion, Path.Combine (resourceDir, "assemblies"), progressReporter, token); - CreateAndroidManifest (apiLevel, version, frameworkVersion, tmanifest, progressReporter, token); - string unaligned = Aapt (aaptPath, tmanifest, resourceDir, packageDir, progressReporter, token); - string unsigned = Path.Combine (packageDir, "unsigned.apk"); - Zipalign (unaligned, unsigned, progressReporter, token); - await ApkSigner (unsigned, tpath, progressReporter, token); - - Directory.CreateDirectory (cacheDir); - File.Copy (tpath, path, overwrite: true); - File.Copy (tmanifest, manifest, overwrite: true); - } finally { - ReportMessage (progressReporter, "Removing temporary directory: {0}", packageDir); - Directory.Delete (packageDir, recursive:true); - ReportEndStep (progressReporter, "Creating " + path); - } - return path; - } - - static void CopyAssemblies (string frameworkVersion, string resourceDir, IProgressNotifier progressReporter, CancellationToken token) - { - ReportBeginStep (progressReporter, "Copying platform assemblies..."); - Directory.CreateDirectory (resourceDir); - string bclDir = MonoDroidSdk.FrameworkPath; - string frameworksDir = Path.GetDirectoryName (bclDir); - string lastDir = null; - foreach (var frameworkDir in Directory.EnumerateDirectories (frameworksDir).OrderBy (ToVersion)) { - lastDir = frameworkDir; - string version = Path.GetFileName (frameworkDir); - if (version == Path.GetFileName (MonoDroidSdk.FrameworkPath)) { - // BCL assemblies aren't part of the Platform Package. - continue; - } - foreach (var pattern in new string [] { "*.dll*", "*.pdb" }) { - foreach (var assembly in Directory.EnumerateFiles (frameworkDir, pattern)) { - token.ThrowIfCancellationRequested (); - string file = Path.GetFileName (assembly); - if (file.StartsWith ("Mono.Android", StringComparison.OrdinalIgnoreCase) && - !file.StartsWith ("Mono.Android.Export", StringComparison.OrdinalIgnoreCase)) - continue; - ReportMessage (progressReporter, "Copying file: {0}", assembly); - File.Copy (assembly, Path.Combine (resourceDir, Path.GetFileName (assembly)), overwrite:true); - } - } - if (version == frameworkVersion) - break; - } - foreach (var lib in Directory.EnumerateFiles (lastDir, "Mono.Android.*")) { - if (Path.GetExtension (lib) == ".xml") - continue; - token.ThrowIfCancellationRequested (); - ReportMessage (progressReporter, "Copying file: {0}", lib); - File.Copy (lib, Path.Combine (resourceDir, Path.GetFileName (lib)), overwrite:true); - } - ReportEndStep (progressReporter, "Copying platform assemblies..."); - } - - static void CreateAndroidManifest (int apiLevel, int version, string frameworkVersion, string androidManifest, IProgressNotifier progressReporter, CancellationToken token) - { - var nsAndroid = XNamespace.Get ("http://schemas.android.com/apk/res/android"); - var doc = new XDocument ( - new XDeclaration ("1.0", "UTF-8", null), - new XElement ("manifest", - new XAttribute (XNamespace.Xmlns + "android", nsAndroid), - new XAttribute ("package", "Mono.Android.Platform.ApiLevel_" + apiLevel), - new XAttribute (nsAndroid + "installLocation", "auto"), - new XAttribute (nsAndroid + "versionCode", version), - new XAttribute (nsAndroid + "versionName", frameworkVersion), - new XElement ("uses-sdk", - new XAttribute (nsAndroid + "minSdkVersion", 4), - new XAttribute (nsAndroid + "targetSdkVersion", apiLevel)), - new XElement ("application", - new XAttribute (nsAndroid + "label", string.Format ("Xamarin.Android API-{0} Support", apiLevel)), - new XAttribute (nsAndroid + "hasCode", "false")))); - ReportMessage (progressReporter, "Creating: {0}", androidManifest); - var utf8 = new UTF8Encoding (encoderShouldEmitUTF8Identifier:false); - using (var o = new StreamWriter (androidManifest, append:false, encoding:utf8)) { - o.NewLine = "\n"; - doc.Save (o); - } - token.ThrowIfCancellationRequested (); - } - - static string Aapt (string aapt, string androidManifest, string resourceDir, string outDir, IProgressNotifier progressReporter, CancellationToken token) - { - // /opt/android/sdk/build-tools/18.0.0/aapt package -f -0 .dll -0 .mdb -M AndroidManifest.xml -I /opt/android/sdk/platforms/android-8/android.jar -F unsigned.apk -k r - string apk = Path.Combine (outDir, "unaligned.apk"); - var arguments = string.Format ("package -f -0 .dll -0 .mdb -M \"{0}\" -I \"{1}\" -F \"{2}\" -k \"{3}\"", - androidManifest, Path.Combine (AndroidSdk.GetLatestPlatformDirectory (), "android.jar"), apk, resourceDir); - var psi = new ProcessStartInfo (aapt, arguments); - ReportMessage (progressReporter, "Creating: {0}", apk); - Exec ("Aapt", psi, progressReporter, token); - return apk; - } - - static void Exec (string step, ProcessStartInfo psi, IProgressNotifier progressReporter, CancellationToken token) - { - ReportMessage (progressReporter, "Executing: {0} {1}", psi.FileName, psi.Arguments); - - TextWriter stdout = Console.Out; - TextWriter stderr = Console.Error; - bool disposeStdout = false; - if (progressReporter != null) { - stdout = new ProgressTextWriter (progressReporter, step); - stderr = stdout; - disposeStdout = true; - } - - stderr = new TeeTextWriter (stderr); - - try { - int r = ProcessUtils.StartProcess (psi, stdout, stderr, token).Result; - ReportMessage (progressReporter, "{0} exited with value: {1}", psi.FileName, r); - - if (r != 0) - throw new InvalidOperationException (string.Format ("'{0}' exited with code '{1}': {2}", psi.FileName, r, stderr.ToString ())); - } finally { - if (disposeStdout) - stdout.Dispose (); - } - } - - static Task ApkSigner (string unsigned, string signed, IProgressNotifier progressReporter, CancellationToken token) - { - // See monodroid/tools/msbuild/Tasks/GetAppSettingsDirectory.cs!Execute() - var keystore = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), - "Xamarin", - "Mono for Android", - "debug.keystore"); - ReportMessage (progressReporter, "Creating: {0}", signed); - var options = new AndroidSigningOptions { - KeyStore = keystore, - KeyAlias = "androiddebugkey", - KeyPass = "android", - StorePass = "android", - }; - return PackageSigningTasks.SignPackageWithApkSignerAsync (options, unsigned, signed, token, AndroidSdk.ApkSignerJar, - result => ReportMessage (progressReporter, result)); - } - - static void Zipalign (string unaligned, string packageFile, IProgressNotifier progressReporter, CancellationToken token) - { - // /opt/android/sdk/tools/zipalign 4 unaligned.apk Xamarin.Android.Platform.apk - var arguments = string.Format ("4 \"{0}\" \"{1}\"", - unaligned, packageFile); - var psi = new ProcessStartInfo (AndroidSdk.ZipAlignExe, arguments); - ReportMessage (progressReporter, "Creating: {0}", packageFile); - Exec ("zipalign", psi, progressReporter, token); - } - - internal static void ReportBeginStep (IProgressNotifier progressReporter, string step) - { - if (progressReporter != null) { - progressReporter.BeginStep (step); - progressReporter.ReportMessage (step); - } - } - - internal static void ReportEndStep (IProgressNotifier progressReporter, string step) - { - if (progressReporter != null) - progressReporter.EndStep (step); - } - - internal static void ReportMessage (IProgressNotifier progressReporter, string format, params object[] args) - { - if (progressReporter != null) { - progressReporter.ReportMessage (string.Format (format, args)); - } - } - } - - class ProgressTextWriter : TextWriter { - - public ProgressTextWriter (IProgressNotifier progressRepoter, string step) - { - ProgressReporter = progressRepoter; - Step = step; - - ProgressReporter.BeginStep (step); - } - - public IProgressNotifier ProgressReporter { get; private set; } - public string Step { get; private set; } - - public override Encoding Encoding { - get { return Encoding.Default; } - } - - StringBuilder message = new StringBuilder (); - - public override void Write (char value) - { - if (value == '\r' || value == '\n') { - if (message.Length > 0) - ProgressReporter.ReportMessage (message.ToString ()); - message.Clear (); - return; - } - message.Append (value); - } - - public override void Write (string value) - { - ProgressReporter.ReportMessage (value); - } - - protected override void Dispose (bool disposing) - { - if (Step == null) - return; - ProgressReporter.EndStep (Step); - Step = null; - base.Dispose (disposing); - } - } - - class TeeTextWriter : StringWriter { - - public TeeTextWriter (TextWriter output) - { - Output = output; - } - - public TextWriter Output { get; private set; } - - public override void Write (char value) - { - base.Write (value); - Output.Write (value); - } - - public override void Write (string value) - { - base.Write (value); - Output.Write (value); - } - - public override void Write (char[] buffer, int index, int count) - { - base.Write (buffer, index, count); - Output.Write (buffer, index, count); - } - } -} diff --git a/src/Xamarin.AndroidTools/Properties/Resources.Designer.cs b/src/Xamarin.AndroidTools/Properties/Resources.Designer.cs index 11f001eefb0..6f8a8b99922 100644 --- a/src/Xamarin.AndroidTools/Properties/Resources.Designer.cs +++ b/src/Xamarin.AndroidTools/Properties/Resources.Designer.cs @@ -60,69 +60,6 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to Alias cannot have characters that are not allowed in file names. - /// - internal static string CreateKeyError_AliasContainsInvalidCharacters { - get { - return ResourceManager.GetString("CreateKeyError_AliasContainsInvalidCharacters", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error importing the key. - /// - internal static string CreateKeyError_ErrorImportingKey { - get { - return ResourceManager.GetString("CreateKeyError_ErrorImportingKey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There was an error creating the key. - /// - internal static string CreateKeyError_GenericError { - get { - return ResourceManager.GetString("CreateKeyError_GenericError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The keystore was not found. - /// - internal static string CreateKeyError_KeyStoreNotFound { - get { - return ResourceManager.GetString("CreateKeyError_KeyStoreNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Validity cannot be less than 1 year. - /// - internal static string CreateKeyError_MinimumOneYearValidity { - get { - return ResourceManager.GetString("CreateKeyError_MinimumOneYearValidity", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to KeyStoreManagement has not been initialized. - /// - internal static string CreateKeyError_NotInitialized { - get { - return ResourceManager.GetString("CreateKeyError_NotInitialized", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unsupported Platform. - /// - internal static string CreateKeyError_UnsupportedPlatform { - get { - return ResourceManager.GetString("CreateKeyError_UnsupportedPlatform", resourceCulture); - } - } - /// /// Looks up a localized string similar to Unexpected error occurred trying to disconnect Jdwp client.. /// diff --git a/src/Xamarin.AndroidTools/Properties/Resources.resx b/src/Xamarin.AndroidTools/Properties/Resources.resx index 07bed6ebf5f..ebdb18844d2 100644 --- a/src/Xamarin.AndroidTools/Properties/Resources.resx +++ b/src/Xamarin.AndroidTools/Properties/Resources.resx @@ -117,27 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Alias cannot have characters that are not allowed in file names - - - There was an error importing the key - - - There was an error creating the key - - - The keystore was not found - - - Validity cannot be less than 1 year - - - KeyStoreManagement has not been initialized - - - Unsupported Platform - Unexpected error occurred trying to disconnect Jdwp client. diff --git a/src/Xamarin.AndroidTools/PublicationUtilities/KeyManagement.cs b/src/Xamarin.AndroidTools/PublicationUtilities/KeyManagement.cs deleted file mode 100644 index b2fcbc4e7b6..00000000000 --- a/src/Xamarin.AndroidTools/PublicationUtilities/KeyManagement.cs +++ /dev/null @@ -1,558 +0,0 @@ -// -// KeyManagement.cs -// -// Author: -// Greg Munn -// -// Copyright (c) 2014 Xamarin Inc -// -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Text; -using Mono.AndroidTools; -using System.Xml.Linq; -using System.Threading; -using Xamarin.AndroidTools.Properties; - -namespace Xamarin.AndroidTools.PublicationUtilities -{ - public static class KeyManagement - { - static string keystoreBaseDir; - - public enum Platform - { - Mac, - Windows - } - - static KeyManagement () - { - if (Mono.AndroidTools.Util.Platform.IsMac) { - Initialize (KeyManagement.Platform.Mac); - } else { - Initialize (KeyManagement.Platform.Windows); - } - } - - /// - /// Used by test fixtures to set an arbitrary keystore folder - /// - internal static void OverrideKeystoreBaseDirectory (string folder) - { - keystoreBaseDir = folder; - } - - static void Initialize (Platform platform) - { - switch (platform) { - case Platform.Mac: - var macHome = Environment.GetFolderPath (Environment.SpecialFolder.UserProfile); - keystoreBaseDir = Path.Combine (macHome, "Library", "Developer", "Xamarin", "Keystore"); - break; - case Platform.Windows: - var winHome = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData); - keystoreBaseDir = Path.Combine (winHome, "Xamarin", "Mono for Android", "Keystore"); - break; - default: - throw new InvalidOperationException (Resources.CreateKeyError_UnsupportedPlatform); - } - } - - public static void DeleteKey (KeystoreEntry key) - { - var dir = Path.GetDirectoryName (key.Keystore); - Directory.Delete (dir, true); - } - - public static Task GetKeyAsync(string keystore) - { - if (string.IsNullOrWhiteSpace(keystore) || !File.Exists(keystore)) - return Task.FromResult(default(KeystoreEntry)); - - CheckInitialized(); - - return Task.Run(() => - { - var alias = Path.GetFileNameWithoutExtension(keystore); - - var aliasInfo = GetAliasInfo(keystore); - if (string.IsNullOrEmpty(aliasInfo.Item2)) - { - return new KeystoreEntry(keystore, alias, aliasInfo.Item1, string.Empty); - } - else - { - return new KeystoreEntry(keystore, alias, aliasInfo.Item1, aliasInfo.Item2); - } - }); - } - - public static Task> ListManagedKeysAsync () - { - CheckInitialized (); - - // iterate over the .keystore files that are in the baedir - var tcs = new TaskCompletionSource> (); - var result = new List (); - int missingInfoCount = 0; - - if (!Directory.Exists (keystoreBaseDir)) { - tcs.SetResult (new List ()); - return tcs.Task; - } - - var keystores = Directory.GetFiles (keystoreBaseDir, "*.keystore", SearchOption.AllDirectories); - foreach (var key in keystores) { - var alias = Path.GetFileNameWithoutExtension (key); - - var aliasInfo = GetAliasInfo (key); - if (string.IsNullOrEmpty (aliasInfo.Item2)) { - // we don't have it, get from keytool and update... - // we lock on the result list to allow tasks to sync info - lock (result) { - missingInfoCount++; - } - - ExtractAliasInfoAsync (key, aliasInfo.Item1).ContinueWith (lt => { - // we have returned with a list of aliases in the key (we expect there should only be one and - // we can now update our creation date info - - lock (result) { - missingInfoCount--; - - if (!lt.IsFaulted) { - foreach (var entry in lt.Result) { - result.Add (entry); - WriteCreationDateInfo (entry.Keystore, entry.CreationDate, entry.ValidityInfo); - } - } - - if (missingInfoCount <= 0) { - tcs.SetResult (result); - } - } - }); - } else { - lock (result) - result.Add (new KeystoreEntry (key, alias, aliasInfo.Item1, aliasInfo.Item2)); - } - } - - lock (result) { - if (missingInfoCount <= 0) { - tcs.SetResult (result); - } - } - - return tcs.Task; - } - - /// - /// Lists the aliases that are in the given keystore. Throws if the password is incorrect. - /// - public static Task> ListKeystoreAliasesAsync (string store, string storePassword) - { - var listTask = PackageSigningTasks.ListKeyStoreAliasesAsync (store, storePassword, CancellationToken.None); - - return listTask.ContinueWith> (t => { - if (t.IsFaulted) { - // observe the error - AndroidLogger.LogError ("keytool", "ListKeytoreAliases - {0}", t.Exception); - - var toolEx = t.Exception.InnerException as AndroidSdkToolException; - if (toolEx != null) { - throw new InvalidOperationException (toolEx.ToolErrorMessage); - } - - throw t.Exception.InnerException; - } - - var aliasInfo = ExtractAliasInfo (t.Result); - return aliasInfo.Select (x => new KeystoreEntry (store, x.Item1, x.Item2, x.Item3)).ToList (); - }); - } - - /// - /// Special version of ListKeystoreAliasesAsync that assumes that there will only be one alias in the keystore because it is a managed keystore. - /// This is to ensure that no matter what happens when we go to get additional information from the keystore that we always return a KeyStoreEntry - /// - static Task> ExtractAliasInfoAsync (string store, DateTime currentCreationTimestamp) - { - var listTask = PackageSigningTasks.ListKeyStoreAliasesAsync (store, null, CancellationToken.None); - - return listTask.ContinueWith> (t => { - if (t.IsFaulted) { - // observe the error - AndroidLogger.LogError ("keytool", "ListKeytoreAliases - {0}", t.Exception); - - var aliasName = Path.GetFileNameWithoutExtension (store); - var entry = new KeystoreEntry (store, aliasName, currentCreationTimestamp, string.Empty); - return new [] { entry }.ToList (); - } - - var aliasInfo = ExtractAliasInfo (t.Result); - - if (aliasInfo.Count > 0) { - return aliasInfo.Select (x => new KeystoreEntry (store, x.Item1, x.Item2, x.Item3)).ToList (); - } - else { - var aliasName = Path.GetFileNameWithoutExtension (store); - var entry = new KeystoreEntry (store, aliasName, currentCreationTimestamp, string.Empty); - return new [] { entry }.ToList (); - } - }); - } - - /// - /// Gets the information about the alias. Returns the detail as is from keytool - /// - public static Task GetAliasDetailAsync (string store, string alias, string storePassword) - { - // we could just return the task you know..... - var listTask = PackageSigningTasks.ListKeyStoreAliasAsync (store, alias, storePassword, CancellationToken.None); - - return listTask.ContinueWith (t => { - if (t.IsFaulted) { - // observe the error - AndroidLogger.LogError ("keytool", "ListKeyStoreAlias - {0}", t.Exception); - - var toolEx = t.Exception.InnerException as AndroidSdkToolException; - if (toolEx != null) { - throw new InvalidOperationException (toolEx.ToolErrorMessage); - } - - throw t.Exception.InnerException; - } - - return t.Result; - }); - } - - /// - /// Creates a key with the given alias and returns the store in which the key was created. - /// - public static Task CreateKeyAsync (string alias, string password, string dname, int validity) - { - if (string.IsNullOrEmpty (alias)) - throw new ArgumentNullException ("alias"); - if (string.IsNullOrEmpty (password)) - throw new ArgumentNullException ("password"); - if (string.IsNullOrEmpty (dname)) - throw new ArgumentNullException ("dname"); - if (validity < 1) - throw new ArgumentException (Resources.CreateKeyError_MinimumOneYearValidity, "validity"); - if (Path.GetInvalidFileNameChars ().Intersect (alias).Any ()) - throw new InvalidDataException (Resources.CreateKeyError_AliasContainsInvalidCharacters); - - var options = new AndroidSigningOptions { - KeyAlias = alias, - KeyPass = password, - KeyStore = CreateStoreFilename (alias), - StorePass = password, - }; - - var keyTask = PackageSigningTasks.GenerateKeyPairAsync (options, dname, validity, CancellationToken.None); - - return keyTask.ContinueWith ((t) => { - if (t.IsFaulted) { - // observe the error - AndroidLogger.LogError ("keytool", "CreateKey - {0}", t.Exception); - - var toolEx = t.Exception.InnerException as AndroidSdkToolException; - if (toolEx != null) { - throw new InvalidOperationException (toolEx.ToolErrorMessage); - } - - throw t.Exception.InnerException; - } - - if (!t.Result) { - // observe the error - AndroidLogger.LogError ("keytool", "CreateKey failed"); - throw new InvalidOperationException (Resources.CreateKeyError_GenericError); - } - - WriteCreationDateInfo (options.KeyStore, DateTime.Today, string.Empty); - return options.KeyStore; - }); - } - - /// - /// Creates a key with the given alias and returns the store in which the key was created. - /// - public static Task CreateKeyAsync (string alias, string password, string commonName, string organizationUnit, string organization, string locality, string state, string country, int validity) - { - var dname = GetDNameFromValues (commonName, organizationUnit, organization, locality, state, country); - - return CreateKeyAsync (alias, password, dname, validity); - } - - /// - /// Imports a given key from a keystore and returns the path to the new store to which it was imported. - /// A new store is guaranteed to be created for the imported key. - /// - public static Task ImportKeyAsync (string keystore, string alias, string storePassword, string aliasPassword, DateTime creationDate) - { - var storeFilename = CreateStoreFilename (alias); - - if (!File.Exists (keystore)) - throw new FileNotFoundException (Resources.CreateKeyError_KeyStoreNotFound, "keystore"); - - var listTask = PackageSigningTasks.ImportKeyAsync (keystore, storePassword, alias, aliasPassword, storeFilename, CancellationToken.None); - - return listTask.ContinueWith ((t) => { - if (t.IsFaulted) { - // observe the error - AndroidLogger.LogError ("keytool", "ImportKey - {0}", t.Exception); - - var toolEx = t.Exception.InnerException as AndroidSdkToolException; - if (toolEx != null) { - throw new InvalidOperationException (toolEx.ToolErrorMessage); - } - - throw t.Exception.InnerException; - } - - if (!t.Result) { - // observe the error - AndroidLogger.LogError ("keytool", "ImportKey failed"); - throw new InvalidOperationException (Resources.CreateKeyError_ErrorImportingKey); - } - - WriteCreationDateInfo (storeFilename, creationDate, string.Empty); - return storeFilename; - }); - } - - public static string GetDNameFromValues (string [] values) - { - var sb = new StringBuilder (); - - for (int i = 0; i < values.Length; i++) { - string value = values [i]; - if (string.IsNullOrEmpty (value)) - continue; - - if (sb.Length > 0) - sb.Append (", "); - - switch (i) { - case 0: sb.Append ("CN="); - break; - case 1: sb.Append ("OU="); - break; - case 2: sb.Append ("O="); - break; - case 3: sb.Append ("L="); - break; - case 4: sb.Append ("S="); - break; - case 5: sb.Append ("C="); - break; - } - sb.Append (GetEscapedDnameValue (value)); - } - - return sb.ToString (); - } - - public static string GetDNameFromValues (string commonName, string organizationUnit, string organization, string locality, string state, string country) - { - return GetDNameFromValues (new [] { commonName, organizationUnit, organization, locality, state, country }); - } - - public static bool IsValidAlias (string alias) - { - if (string.IsNullOrEmpty (alias)) - return false; - - if (Path.GetInvalidFileNameChars ().Intersect (alias).Any ()) - return false; - - return true; - } - - public static string GetErrorText (AggregateException ex) - { - if (ex.InnerExceptions.Count == 1) { - var message = ex.InnerExceptions [0].Message; - - message = message.Replace ("keytool error: java.io.IOException:", string.Empty).Trim (); - - return message; - } - - return ex.Message; - } - - static string GetEscapedDnameValue (string value) - { - return value.Replace (@",", @"\,"); - } - - static string CreateStoreFilename (string alias) - { - CheckInitialized (); - - var aliasDir = CreateStoreDirectory (keystoreBaseDir, alias); - - return Path.Combine (aliasDir, alias + ".keystore"); - } - - static string CreateStoreDirectory (string baseDir, string alias) - { - string aliasDir; - int unique = 1; - string name; - - do { - if (unique > 1) - name = string.Format ("{0} - {1}", alias, unique); - else - name = alias; - - aliasDir = Path.Combine (baseDir, name); - unique++; - } while (Directory.Exists (aliasDir)); - - Directory.CreateDirectory (aliasDir); - return aliasDir; - } - - static Tuple GetAliasInfo (string keystoreFile) - { - var keystoreInfoFile = Path.ChangeExtension (keystoreFile, ".keyInfo"); - - if (File.Exists (keystoreInfoFile)) { - try { - var doc = XDocument.Load (keystoreInfoFile); - var creationDate = (DateTime)doc.Root.Element ("CreationDate"); - var validityInfo = (string)doc.Root.Element ("ValidityInfo"); - return new Tuple (creationDate, validityInfo); - } - catch { - return new Tuple (DateTime.MinValue, string.Empty); - } - } - - return new Tuple (DateTime.MinValue, string.Empty); - } - - static void WriteCreationDateInfo (string keystoreFile, DateTime creationDate, string validityInfo) - { - try { - var keystoreInfoFile = Path.ChangeExtension (keystoreFile, ".keyInfo"); - - var doc = new XDocument (); - doc.Add (new XElement ("KeyStore")); - doc.Root.Add (new XElement ("CreationDate", creationDate)); - doc.Root.Add (new XElement ("ValidityInfo", validityInfo)); - - doc.Save (keystoreInfoFile); - } - catch (Exception ex) { - AndroidLogger.LogError ("KeyStoreManagement - WriteCreationInfo", ex); - } - } - - /// - /// Extracts the alias names, creation dates and validity from the keytool -list -v command output - /// - public static List> ExtractAliasInfo (string listOutput) - { - /* list output looks a little like this - Keystore type: JKS - Keystore provider: SUN - - Your keystore contains 2 entries - - Alias name: Alias 1 - Creation date: Aug 14, 2014 - Entry type: PrivateKeyEntry - Certificate chain length: 1 - Certificate[1]: - Owner: CN=Me - Issuer: CN=Me - Serial number: 53ecc39d - Valid from: Thu Aug 14 10:11:41 EDT 2014 until: Sat Sep 13 10:11:41 EDT 2014 - Certificate fingerprints: - MD5: 48:21:C7:99:74:FC:43:41:9B:52:EA:98:78:86:49:AD - SHA1: 3C:7E:3F:FE:26:9B:7B:76:1E:CF:84:AF:0F:92:69:B5:07:B9:24:03 - Signature algorithm name: SHA1withRSA - Version: 3 - - - ******************************************* - ******************************************* - - and then repeats - - because of localisatin from the output, we will have to rely on non-localisable output to find the information we want - this will be the ": PrivateKeyEntry" and the "*******************************************"'s - */ - - const string privateKeyEntryTag = ": PrivateKeyEntry"; - const string starsTag = "*******************************************"; - - var result = new List> (); - - var lines = listOutput.Split (new [] { "\n" }, StringSplitOptions.RemoveEmptyEntries); - - string alias = null; - string creationDateText = null; - string validityText = null; - DateTime creationDate = DateTime.MinValue; - - for (int lineNumber = 0; lineNumber < lines.Length; lineNumber++) { - var line = lines [lineNumber]; - - if (line.IndexOf (privateKeyEntryTag, StringComparison.InvariantCultureIgnoreCase) != -1) { - // we are on a line containing the PrivateKeyEntry tag - // look behind to the alias name and creation date - if (lineNumber > 2) { - var aliasLine = lines [lineNumber - 2]; - var creationInfoLine = lines [lineNumber - 1]; - - var ix = aliasLine.IndexOf (":", StringComparison.InvariantCultureIgnoreCase); - alias = aliasLine.Substring (ix + 1).Trim (); - - ix = creationInfoLine.IndexOf (":", StringComparison.InvariantCultureIgnoreCase); - creationDateText = creationInfoLine.Substring (ix + 1).Trim (); - } - - if (lineNumber < lines.Length - 6) { - var validityLine = lines [lineNumber + 6]; - - var ix = validityLine.IndexOf (":", StringComparison.InvariantCultureIgnoreCase); - validityText = validityLine.Substring (ix + 1).Trim (); - } - - if (alias != null) { - if (!DateTime.TryParse (creationDateText, out creationDate)) - creationDate = DateTime.MinValue; - result.Add (new Tuple (alias, creationDate, validityText)); - } - } - - if (line.IndexOf (starsTag, StringComparison.InvariantCultureIgnoreCase) != -1) { - alias = null; - creationDateText = null; - validityText = null; - creationDate = DateTime.MinValue; - } - } - - return result; - } - - static void CheckInitialized () - { - if (keystoreBaseDir == null) - throw new InvalidOperationException (Resources.CreateKeyError_NotInitialized); - } - } -} diff --git a/src/Xamarin.AndroidTools/PublicationUtilities/KeystoreEntry.cs b/src/Xamarin.AndroidTools/PublicationUtilities/KeystoreEntry.cs deleted file mode 100644 index ef05f5ef26c..00000000000 --- a/src/Xamarin.AndroidTools/PublicationUtilities/KeystoreEntry.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// KeystoreEntry.cs -// -// Author: -// Greg Munn -// -// Copyright (c) 2014 Xamarin Inc -// - -using System; - -namespace Xamarin.AndroidTools.PublicationUtilities -{ - public sealed class KeystoreEntry - { - public KeystoreEntry (string keystore, string alias, DateTime creationDate, string validityInfo) - { - Keystore = keystore; - Alias = alias; - CreationDate = creationDate; - ValidityInfo = validityInfo; - } - - public string Keystore { get; private set; } - - public string Alias { get; private set; } - - public DateTime CreationDate { get; private set; } - - public string ValidityInfo { get; private set; } - } -} diff --git a/src/Xamarin.AndroidTools/PublicationUtilities/PackageSigningTasks.cs b/src/Xamarin.AndroidTools/PublicationUtilities/PackageSigningTasks.cs deleted file mode 100644 index 27ec7c8e1ac..00000000000 --- a/src/Xamarin.AndroidTools/PublicationUtilities/PackageSigningTasks.cs +++ /dev/null @@ -1,462 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Mono.AndroidTools.Util; -using System.Linq; - -namespace Xamarin.AndroidTools.PublicationUtilities -{ - public static class PackageSigningTasks - { - // simply returns true, assuming the process exit code is zero, then the output is irrelevant - private static Func defaultOutputParser = (s) => true; - - /// - /// Queries the given apk and determines if it is signed or not - /// - public static Task QueryPackageSignatureAsync (string apkFile, CancellationToken token) - { - return QueryPackageSignatureAsync (apkFile, token, AndroidSdk.JarsignerExe); - } - - /// - /// Queries the given apk and determines if it is signed or not - /// - public static Task QueryPackageSignatureAsync (string apkFile, CancellationToken token, string jarsigner) - { - // we need to check the output to see if the package signature was verified or not - Func outputParser = (output) => output.Contains ("verified"); - - var args = new ProcessArgumentBuilder (); - args.Add ("-verify"); - args.AddQuoted (apkFile); - - var toolTask = ProcessUtils.ExecuteToolAsync (jarsigner, args, outputParser, token); - return toolTask; - } - - /// - /// Signs the .APK asynchronously - /// - public static Task SignPackageAsync (AndroidSigningOptions options, string unsignedApk, string signedApk, CancellationToken token) - { - var apkSigner = AndroidSdk.ApkSignerJar; - if (apkSigner != null) { - // use apk signer instead if it exists - return SignPackageWithApkSignerAsync (options, unsignedApk, signedApk, token, apkSigner); - } - - return SignPackageAsync (options, unsignedApk, signedApk, token, AndroidSdk.JarsignerExe); - } - - /// - /// Signs the .APK asynchronously - /// - public static Task SignPackageAsync (AndroidSigningOptions options, string unsignedApk, string signedApk, CancellationToken token, string jarsigner) - { - // Create our Argument list - var args = new ProcessArgumentBuilder (); - args.Add ("-keystore"); - args.AddQuoted (options.KeyStore); - args.Add ("-storepass"); - args.AddQuoted (options.StorePass); - args.Add ("-keypass"); - args.AddQuoted (options.KeyPass); - - if (!string.IsNullOrEmpty (options.TsaUrl)) { - args.Add ("-tsa"); - args.AddQuoted (options.TsaUrl); - } - - args.Add ("-digestalg"); - - if(options.SigningAlgorithm == PackageSigningAlgorithm.SHA256withRSA) - args.Add("SHA-256"); - else - args.Add ("SHA1"); - - args.Add ("-sigalg"); - - switch (options.SigningAlgorithm) { - case PackageSigningAlgorithm.RSA: - args.Add ("md5withRSA"); - break; - case PackageSigningAlgorithm.DSA: - args.Add ("SHA1withDSA"); - break; - case PackageSigningAlgorithm.SHA256withRSA: - args.Add("SHA256withRSA"); - break; - default: - args.Add ("md5withRSA"); - break; - } - - args.Add ("-signedjar"); - args.AddQuoted (signedApk, unsignedApk, options.KeyAlias); - - var toolTask = ProcessUtils.ExecuteToolAsync (jarsigner, args, defaultOutputParser, token); - return toolTask; - } - - /// - /// Signs the .APK asynchronously - /// - public static Task SignPackageWithApkSignerAsync (AndroidSigningOptions options, string unsignedApk, string signedApk, CancellationToken token, string apksigner) => - SignPackageWithApkSignerAsync (options, unsignedApk, signedApk, token, apksigner, null); - - /// - /// Signs the .APK asynchronously with logging - /// - public static Task SignPackageWithApkSignerAsync (AndroidSigningOptions options, string unsignedApk, string signedApk, CancellationToken token, string apksigner, Action logMessage) - { - apksigner = apksigner ?? AndroidSdk.ApkSignerJar; - bool useJava = apksigner != null && apksigner.EndsWith (".jar", StringComparison.OrdinalIgnoreCase); - - // Create our Argument list - var fileName = useJava ? AndroidSdk.JavaExe : apksigner; - var args = new ProcessArgumentBuilder (); - if (useJava) { - args.Add ("-jar"); - args.AddQuoted (apksigner); - } - args.Add ("sign"); - args.Add ("--ks"); - args.AddQuoted (options.KeyStore); - args.Add ("--ks-key-alias"); - args.AddQuoted (options.KeyAlias); - args.Add ("--out"); - args.AddQuoted (signedApk); - args.Add ("--ks-pass"); - args.AddQuoted ("pass:" + options.StorePass); - args.Add ("--key-pass"); - args.AddQuoted ("pass:"+ options.KeyPass); - - if (options.MinSdkVersion != null) { - args.Add ("--min-sdk-version"); - args.Add (options.MinSdkVersion.ToString ()); - } - - //if (!string.IsNullOrEmpty (options.TsaUrl)) - //{ - // args.Add ("-tsa"); - // args.AddQuoted (options.TsaUrl); - //} - - //args.Add ("-digestalg"); - //args.Add ("SHA1"); - //args.Add ("-sigalg"); - - //switch (options.SigningAlgorithm) - //{ - //case PackageSigningAlgorithm.RSA: - // args.Add ("md5withRSA"); - // break; - //case PackageSigningAlgorithm.DSA: - // args.Add ("SHA1withDSA"); - // break; - //default: - // args.Add ("md5withRSA"); - // break; - //} - - args.AddQuoted (unsignedApk); - - logMessage?.Invoke ($"Executing: {fileName} {args}"); - return ProcessUtils.ExecuteToolAsync (fileName, args, result => { - if (!string.IsNullOrEmpty (result)) - logMessage?.Invoke ($"apksigner: {result}"); - return true; - }, token); - } - - /// - /// Attempts to determine the signing algorithm to use with the given keystore and alias - /// - public static Task DetermineSigningAlgorithm (string keystore, string alias, CancellationToken token) - { - return DetermineSigningAlgorithm (keystore, alias, token, AndroidSdk.KeyToolExe); - } - - /// - /// Attempts to determine the signing algorithm to use with the given keystore and alias - /// - public static Task DetermineSigningAlgorithm (string keystore, string alias, CancellationToken token, string keytool) - { - var args = new ProcessArgumentBuilder (); - args.Add ("-list"); - args.Add ("-v"); - args.Add ("-keystore"); - args.AddQuoted (keystore); - args.Add ("-alias"); - args.AddQuoted (alias); - // make sure the output is in english - args.Add ("-J\"-Duser.language=en-US\""); - - Action onStarted = (p) => { - // press enter when prompted to enter password - p.StandardInput.WriteLine (); - }; - - var aliasDetailsTask = ProcessUtils.ExecuteToolAsync (keytool, args, (s) => s, token, onStarted); - return aliasDetailsTask.ContinueWith (dt => { - var lines = dt.Result.Split (new [] { "\n" }, StringSplitOptions.RemoveEmptyEntries); - var algLine = lines.FirstOrDefault (l => l.Contains ("Signature algorithm name:")); - - if (!string.IsNullOrEmpty (algLine)) { - var parts = algLine.Trim().Split (new [] { "Signature algorithm name:" }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length > 0) { - return FromString (parts [0]); - } - } else { - // mmm, unexpected - Mono.AndroidTools.AndroidLogger.LogWarning (string.Format ("Could not determine signing algorithm: {0}", dt.Result)); - } - - return PackageSigningAlgorithm.Unsupported; - }, TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.NotOnCanceled ); - } - - static PackageSigningAlgorithm FromString (string alg) - { - Mono.AndroidTools.AndroidLogger.LogInfo (string.Format ("Converting signing algorithm from {0}", alg)); - - if (!string.IsNullOrEmpty (alg)) { - var parts = alg.Split (new [] { "with" }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length == 2) { - switch (parts [1]) { - case "DSA": - return PackageSigningAlgorithm.DSA; - case "RSA": - return PackageSigningAlgorithm.RSA; - } - } - } - - return PackageSigningAlgorithm.Unsupported; - } - - /// - /// Aligns the .APK asynchronously - /// - public static Task AlignPackageAsync (string srcApk, string destApk, CancellationToken token) - { - return AlignPackageAsync (srcApk, destApk, token, AndroidSdk.ZipAlignExe); - } - - /// - /// Aligns the .APK asynchronously - /// - public static Task AlignPackageAsync (string srcApk, string destApk, CancellationToken token, string zipAlignExe) - { - // Create our Argument list - var args = new ProcessArgumentBuilder (); - args.Add ("-f"); - args.Add ("4"); - args.AddQuoted (srcApk, destApk); - - var toolTask = ProcessUtils.ExecuteToolAsync (zipAlignExe, args, defaultOutputParser, token); - return toolTask; - } - - /// - /// Verifies the alignment of the .APK asynchronously - /// - public static Task VerifyPackageAlignmentAsync (string apkfileName, CancellationToken token) - { - return VerifyPackageAlignmentAsync (apkfileName, token, AndroidSdk.ZipAlignExe); - } - - /// - /// Verifies the alignment of the .APK asynchronously - /// - public static Task VerifyPackageAlignmentAsync (string apkfileName, CancellationToken token, string zipAlignExe) - { - // Create our Argument list - var args = new ProcessArgumentBuilder (); - args.Add ("-c"); - args.Add ("4"); - args.AddQuoted (apkfileName); - - var toolTask = ProcessUtils.ExecuteToolAsync (zipAlignExe, args, defaultOutputParser, token); - return toolTask; - } - - /// - /// Generates a key-pair asynchronously - /// - public static Task GenerateKeyPairAsync (AndroidSigningOptions options, string dname, int validity, CancellationToken token) - { - return GenerateKeyPairAsync (options, dname, validity, token, AndroidSdk.KeyToolExe); - } - - /// - /// Generates a key-pair asynchronously - /// - public static Task GenerateKeyPairAsync (AndroidSigningOptions options, string dname, int validity, CancellationToken token, string keytool) - { - // For compatibility with JDK > 1.8, which errors out on an empty `-dname` value - if (string.IsNullOrEmpty (dname)) - dname = "CN="; - - var dnameParameter = ProcessArgumentBuilder.Quote(dname); - - if (OS.IsWindows) - dnameParameter = dnameParameter.Replace(",", @"\,"); - - var args = new ProcessArgumentBuilder (); - args.Add ("-genkeypair"); - args.Add ("-alias"); - args.AddQuoted (options.KeyAlias); - args.Add ("-dname"); - args.Add (dnameParameter); - args.Add ("-storepass"); - args.AddQuoted (options.StorePass); - args.Add ("-keypass"); - args.AddQuoted (options.KeyPass); - args.Add ("-keystore"); - args.AddQuoted (options.KeyStore); - args.Add ("-keysize"); - args.Add ("2048"); - args.Add ("-keyalg"); - args.Add ("RSA"); - if (validity > 0) { - args.Add ("-validity"); - args.AddQuoted (validity.ToString ()); - } - - var toolTask = ProcessUtils.ExecuteToolAsync (keytool, args, defaultOutputParser, token); - return toolTask; - } - - /// - /// Verifies a key-pair asynchronously - /// - public static Task VerifyKeyPairAsync (AndroidSigningOptions options, CancellationToken token) - { - return VerifyKeyPairAsync (options, token, AndroidSdk.KeyToolExe); - } - - /// - /// Verifies a key-pair asynchronously. Note: options.AliasPass is not used, only the other values - /// - public static Task VerifyKeyPairAsync (AndroidSigningOptions options, CancellationToken token, string keytool) - { - var args = new ProcessArgumentBuilder (); - args.Add ("-list"); - args.AddQuoted ("-keystore", options.KeyStore); - args.AddQuoted ("-storepass", options.StorePass); - args.AddQuoted ("-alias", options.KeyAlias); - - var toolTask = ProcessUtils.ExecuteToolAsync (keytool, args, defaultOutputParser, token); - return toolTask; - } - - /// - /// Lists the aliases that are stored in the keystore, returns the raw output from keytool - /// - public static Task ListKeyStoreAliasesAsync (string keystore, string storePassword, CancellationToken token) - { - return ListKeyStoreAliasesAsync (keystore, storePassword, token, AndroidSdk.KeyToolExe); - } - - /// - /// Lists the aliases that are stored in the keystore, returns the raw output from keytool - /// - public static Task ListKeyStoreAliasesAsync (string keystore, string storePassword, CancellationToken token, string keytool) - { - var args = new ProcessArgumentBuilder (); - args.Add ("-list"); - args.Add ("-v"); - if (!string.IsNullOrEmpty (storePassword)) { - args.Add ("-storepass"); - args.AddQuoted (storePassword); - } - args.Add ("-keystore"); - args.AddQuoted (keystore); - - Action onStarted = (p) => { - // press enter when prompted to enter password - if (string.IsNullOrEmpty (storePassword)) - p.StandardInput.WriteLine (); - }; - - var toolTask = ProcessUtils.ExecuteToolAsync (keytool, args, (s) => s, token, onStarted); - return toolTask; - } - - /// - /// Performs a list of a specific alias within a keystore asynchronously. - /// Used to get information about a specific alias. - /// - public static Task ListKeyStoreAliasAsync (string keystore, string alias, string storePassword, CancellationToken token) - { - return ListKeyStoreAliasAsync (keystore, alias, storePassword, token, AndroidSdk.KeyToolExe); - } - - /// - /// Performs a list of a specific alias within a keystore asynchronously. - /// Used to get information about a specific alias - /// - public static Task ListKeyStoreAliasAsync (string keystore, string alias, string storePassword, CancellationToken token, string keytool) - { - var args = new ProcessArgumentBuilder (); - args.Add ("-list"); - args.Add ("-v"); - if (!string.IsNullOrEmpty (storePassword)) { - args.Add ("-storepass"); - args.AddQuoted (storePassword); - } - args.Add ("-keystore"); - args.AddQuoted (keystore); - args.Add ("-alias"); - args.AddQuoted (alias); - - Action onStarted = (p) => { - // press enter when prompted to enter password - if (string.IsNullOrEmpty (storePassword)) - p.StandardInput.WriteLine (); - }; - - var toolTask = ProcessUtils.ExecuteToolAsync (keytool, args, (s) => s, token, onStarted); - return toolTask; - } - - /// - /// Imports the key from sourceKeystore to destKeystore but uses the same password for the new store - /// - public static Task ImportKeyAsync (string sourceKeystore, string sourceStorePassword, string alias, string aliasPassword, string destKeystore, CancellationToken token) - { - return ImportKeyAsync (sourceKeystore, sourceStorePassword, alias, aliasPassword, destKeystore, token, AndroidSdk.KeyToolExe); - } - - /// - /// Imports the key from sourceKeystore to destKeystore but uses the same password for the new store - /// - public static Task ImportKeyAsync (string sourceKeystore, string sourceStorePassword, string alias, string aliasPassword, string destKeystore, CancellationToken token, string keytool) - { - var args = new ProcessArgumentBuilder (); - args.Add ("-importkeystore"); - args.Add ("-srckeystore"); - args.AddQuoted (sourceKeystore); - args.Add ("-destkeystore"); - args.AddQuoted (destKeystore); - - args.Add ("-srcstorepass"); - args.AddQuoted (sourceStorePassword); - args.Add ("-srcalias"); - args.AddQuoted (alias); - args.Add ("-srckeypass"); - args.AddQuoted (aliasPassword); - - args.Add ("-deststorepass"); - args.AddQuoted (aliasPassword); - args.Add ("-destkeypass"); - args.AddQuoted (aliasPassword); - - var toolTask = ProcessUtils.ExecuteToolAsync (keytool, args, defaultOutputParser, token); - return toolTask; - } - } -} diff --git a/src/Xamarin.AndroidTools/PublicationUtilities/PublishAndroidApplication.cs b/src/Xamarin.AndroidTools/PublicationUtilities/PublishAndroidApplication.cs deleted file mode 100644 index 8c78741b9c8..00000000000 --- a/src/Xamarin.AndroidTools/PublicationUtilities/PublishAndroidApplication.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Mono.AndroidTools; -using Xamarin.AndroidTools.PublicationUtilities; - -namespace Xamarin.AndroidTools -{ - /// - /// Legacy packaging tasks - /// - public static class PublishAndroidApplication - { - /// - /// Signs the .APK asynchronously but does not return a faulted task - /// - public static Task SignPackage (AndroidSigningOptions options, string unsignedApk, string signedApk, StringWriter output, CancellationToken token) - { - if (token.CanBeCanceled && token.IsCancellationRequested) - throw new OperationCanceledException (); - - return PackageSigningTasks.SignPackageAsync (options, unsignedApk, signedApk, token).ContinueWith (res => { - return HandleAsyncTaskAndReturnBool (res, "SignPackageAsync faulted", output); - }); - } - - /// - /// Aligns the .APK asynchronously but does not return a faulted task - /// - public static Task AlignPackage (string srcApk, string destApk, StringWriter output, CancellationToken token) - { - if (token.CanBeCanceled && token.IsCancellationRequested) - throw new OperationCanceledException (); - - return PackageSigningTasks.AlignPackageAsync (srcApk, destApk, token).ContinueWith (res => { - return HandleAsyncTaskAndReturnBool (res, "AlignPackageAsync faulted", output); - }); - } - - /// - /// Generates a key-pair asynchronously but does not return a faulted task - /// - public static Task GenerateKeyPair (AndroidSigningOptions options, string dname, int validity, StringWriter output, CancellationToken token) - { - if (token.CanBeCanceled && token.IsCancellationRequested) - throw new OperationCanceledException (); - - return PackageSigningTasks.GenerateKeyPairAsync (options, dname, validity, token).ContinueWith (res => { - return HandleAsyncTaskAndReturnBool (res, "GenerateKeyPairAsync faulted", output); - }); - } - - /// - /// Verifies a key-pair asynchronously but does not return a faulted task - /// - public static Task VerifyKeyPair (AndroidSigningOptions options, StringWriter output, CancellationToken token) - { - if (token.CanBeCanceled && token.IsCancellationRequested) - throw new OperationCanceledException (); - - if (output != null) - output.WriteLine ("Keystore verification failed:"); - - return PackageSigningTasks.VerifyKeyPairAsync (options, token).ContinueWith (res => { - return HandleAsyncTaskAndReturnBool (res, "VerifyKeyPairAsync faulted", output); - }); - } - - /// - /// Handles the task and returns true or false, logs any exceptions and returns false for cancelled tasks. - /// Preserves API semantics for the methods not suffixed with 'Async' - /// - private static bool HandleAsyncTaskAndReturnBool (Task task, string faultMesage, StringWriter output) - { - if (task.IsFaulted) { - // observe the exception - AndroidLogger.LogError (faultMesage, task.Exception); - if (output != null) { - var toolException = task.Exception.InnerException as AndroidSdkToolException; - if (toolException != null) { - output.Write (toolException.ToolErrorMessage); - } - } - return false; - } - - if (task.IsCanceled) { - return false; - } - - return task.Result; - } - } -} diff --git a/src/Xamarin.AndroidTools/Sessions/AndroidCommandSession.cs b/src/Xamarin.AndroidTools/Sessions/AndroidCommandSession.cs deleted file mode 100644 index 2e8191dc897..00000000000 --- a/src/Xamarin.AndroidTools/Sessions/AndroidCommandSession.cs +++ /dev/null @@ -1,320 +0,0 @@ -// -// AndroidCommandConnection.cs -// -// Author: -// Rodrigo Moya -// Stephen Shaw -// -// Based on Xamarin.MacDev.IPhoneCommandConnection -// Author: -// Michael Hutchinson -// Rolf Bjarne Kvinge -// -// Copyright (c) 2011-2014 Xamarin Inc. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using Mono.AndroidTools; -using Mono.AndroidTools.Util; - -namespace Xamarin.AndroidTools -{ - public abstract class AndroidCommandSession : IDisposable - { - readonly object commandSessionLock = new object (); - readonly List streams = new List (); - Stream reusableStream; - bool disposed; - bool connected_once; - - public IPAddress Address { - get; - protected set; - } - - public int Port { - get; - protected set; - } - - protected AndroidCommandSession (IPAddress ipAddress, int port = 0) - { - Address = ipAddress ?? IPAddress.Loopback; - Port = port; - } - - #region Internal command execution methods - - protected abstract IAsyncResult BeginConnectStream (AsyncCallback callback, object state); - protected abstract Stream EndConnectStream (IAsyncResult result); - - IAsyncResult BeginExecuteCommand (string command, bool consumeStream, AsyncCallback callback = null, object state = null) - { - var data = Encoding.UTF8.GetBytes (command); - if (data.Length > byte.MaxValue) - throw new ArgumentException (string.Format ("Command '{0}' has length {1}, which exceeds maximum length {2}", command, data.Length, byte.MaxValue), "command"); - - var buffer = new byte [data.Length + 1]; - buffer [0] = (byte) data.Length; - Array.Copy (data, 0, buffer, 1, data.Length); - - var ar = new CommandAsyncResult (callback, state) { - Buffer = buffer, - ConsumeStream = consumeStream, - }; - - //try to re-use an existing stream - lock (commandSessionLock) { - if (reusableStream != null) { - ar.Stream = reusableStream; - //if we're going to consume the stream, don't leave it around for others to re-use - if (consumeStream) { - reusableStream = null; - } - } - } - - if (ar.Stream != null) { - ExecuteCommand_BeginWriteCommand (ar); - } else { - BeginConnectStream (ExecuteCommand_ConnectedCommandStream, ar); - } - - return ar; - } - - void ExecuteCommand_ConnectedCommandStream (IAsyncResult ar) - { - var r = (CommandAsyncResult) ar.AsyncState; - try { - r.Stream = EndConnectStream (ar); - ExecuteCommand_BeginWriteCommand (r); - } catch (Exception ex) { - r.CompleteWithError (ex); - } - } - - static void ExecuteCommand_DiscardStream (Stream stream) - { - var discard = new byte [] { 7, (byte) 'd', (byte) 'i', (byte) 's', (byte) 'c', (byte) 'a', (byte) 'r', (byte) 'd' }; - stream.BeginWrite (discard, 0, discard.Length, ar => ((Stream)ar.AsyncState).Dispose (), stream); - } - - void ExecuteCommand_BeginWriteCommand (CommandAsyncResult r) - { - lock (commandSessionLock) - connected_once = true; - r.Stream.BeginWrite (r.Buffer, 0, r.Buffer.Length, ExecuteCommand_WroteCommand, r); - } - - void ExecuteCommand_WroteCommand (IAsyncResult ar) - { - var r = (CommandAsyncResult) ar.AsyncState; - try { - r.Stream.EndWrite (ar); - //if the stream can be re-used, keep it - if (!r.ConsumeStream) { - lock (commandSessionLock) { - if (reusableStream == null) { - reusableStream = r.Stream; - streams.Add (r.Stream); - r.Stream = null; - } - } - //if there was already a re-usable stream from another thread, discard this one - if (r.Stream != null && r.Stream != reusableStream) { - ExecuteCommand_DiscardStream (r.Stream); - } - } - r.Complete (); - } catch (Exception ex) { - r.CompleteWithError (ex); - } - } - - void CancelExecuteCommand (IAsyncResult asyncResult) - { - ((CommandAsyncResult)asyncResult).Cancel (); - } - - Stream EndExecuteCommand (IAsyncResult result) - { - var r = (CommandAsyncResult) result; - r.CheckError (); - return r.ConsumeStream? r.Stream : null; - } - - IAsyncResult BeginSendSkipDebugger (AsyncCallback callback = null, object state = null) - { - return BeginExecuteCommand ("start debugger: no", consumeStream: false, callback: callback, state: state); - } - - void WriteProfilerOutputToFile (Stream stream, string outputFile) - { - try { - using (var fs = new FileStream (outputFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) { - stream.CopyTo (fs); - } - } catch (Exception ex) { - AndroidLogger.LogWarning ("Exception in the profile-data reading thread: {0}", ex); - } - - Console.WriteLine ("Finished writer thread"); - } - - #endregion - - #region CommandAsyncResult - - class CommandAsyncResult : AggregateAsyncResult - { - public CommandAsyncResult (AsyncCallback callback, object state) : base (callback, state) - { - } - - public void Cancel () - { - CompleteWithError (new OperationCanceledException ()); - if (Stream != null) { - Stream.Dispose (); - Stream = null; - } - } - - public byte[] Buffer; - public bool ConsumeStream; - public Stream Stream; - } - - #endregion - - #region Public API - - public bool IsConnected { - get { - lock (commandSessionLock) { - return connected_once; - } - } - } - - public IAsyncResult BeginHandshake (AsyncCallback callback = null, object state = null) - { - var r = new AggregateAsyncResult (callback, state); - BeginExecuteCommand ("ping", false, Handshake_SentPing, r); - return r; - } - - void Handshake_SentPing (IAsyncResult ar) - { - var r = (AggregateAsyncResult) ar.AsyncState; - try { - EndExecuteCommand (ar); - //we know that there will be a reusable string if the ping succeeded - //and nothing should be touching the tream during our handshake - r.Arg1 = reusableStream; - r.Arg2 = new byte[5]; - r.Arg1.BeginReadFull (r.Arg2, 0, r.Arg2.Length, Handshake_ReadPong, r); - } catch (Exception ex) { - r.CompleteWithError (ex); - } - } - - static void Handshake_ReadPong (IAsyncResult ar) - { - var r = (AggregateAsyncResult) ar.AsyncState; - try { - r.Arg1.EndReadFull (ar); - var pong = Encoding.ASCII.GetString (r.Arg2); - if (pong != "pong\0") - throw new Exception ("Bad handshake: '" + pong + "'"); - r.Complete(); - } catch (Exception ex) { - r.CompleteWithError (ex); - } - } - - public void EndHandshake (IAsyncResult result) - { - var r = (AggregateAsyncResult) result; - r.CheckError (); - } - - public void StartLogProfiler (string outputFile, string profilerConfiguration, string heapshotMode, AsyncCallback callback = null, object state = null) - { - EndExecuteCommand (BeginSendSkipDebugger ()); - - var outputDir = Path.GetDirectoryName (outputFile); - Directory.CreateDirectory (outputDir); - - var stream = EndExecuteCommand (BeginExecuteCommand ("start profiler: " + profilerConfiguration, true, callback, state)); - new Thread (() => { - try { - WriteProfilerOutputToFile (stream, outputFile); - } finally { - stream.Dispose (); - } - }) { - IsBackground = true, - Name = "Profiler output writer" - }.Start (); - } - - public void Stop () - { - try { - // Don't try forever to send 'exit process', the user might never have - // started the app on the device. - var exit = true; - - lock (commandSessionLock) { - exit = connected_once; - } - - if (exit && !disposed) { - var ar = BeginExecuteCommand ("exit process", consumeStream: true); - ar.AsyncWaitHandle.WaitOne (100); - EndExecuteCommand (ar); - } - } catch (ObjectDisposedException) { - } catch (SocketException ex) { - Console.WriteLine ("Error while requesting the application to exit: " + ex.Message); - } finally { - Dispose (); // make sure everything is cleaned up - } - } - - #endregion - - #region IDisposable implementation - - ~AndroidCommandSession () - { - Dispose (false); - } - - public void Dispose () - { - if (disposed) - return; - - lock (commandSessionLock) { - if (disposed) - return; - disposed = true; - GC.SuppressFinalize (this); - } - - Dispose (true); - } - - protected abstract void Dispose (bool disposing); - - #endregion - } -} diff --git a/src/Xamarin.AndroidTools/Sessions/AndroidConnectCommandSession.cs b/src/Xamarin.AndroidTools/Sessions/AndroidConnectCommandSession.cs deleted file mode 100644 index 8aebf97c323..00000000000 --- a/src/Xamarin.AndroidTools/Sessions/AndroidConnectCommandSession.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// AndroidConnectCommandConnection.cs -// -// Author: -// Stephen Shaw -// -// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; - -namespace Xamarin.AndroidTools -{ - class AndroidConnectCommandSession : AndroidCommandSession - { - Socket client; - - public AndroidConnectCommandSession (IPAddress ipAddress = null, int port = 0) : base (ipAddress, port) - { - client = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - client.ExclusiveAddressUse = false; - } - - protected override IAsyncResult BeginConnectStream (AsyncCallback callback, object state) - { - return client.BeginConnect (new IPEndPoint (Address, Port), callback, state); - } - - protected override Stream EndConnectStream (IAsyncResult result) - { - client.EndConnect (result); - client.NoDelay = true; - return new NetworkStream (client, true); - } - - protected override void Dispose (bool disposing) - { - if (disposing && client != null) { - client.Dispose (); - client = null; - } - } - } -} diff --git a/src/Xamarin.AndroidTools/Sessions/AndroidDeploySession.cs b/src/Xamarin.AndroidTools/Sessions/AndroidDeploySession.cs deleted file mode 100644 index 5a621424864..00000000000 --- a/src/Xamarin.AndroidTools/Sessions/AndroidDeploySession.cs +++ /dev/null @@ -1,1054 +0,0 @@ -// -// AndroidDeploySession.cs -// -// Authors: -// Jonathan Pobst -// Michael Hutchinson -// -// Copyright 2012-2013 Xamarin Inc. All rights reserved. -// - -using System; -using System.IO; -using System.Threading; -using System.Collections.Generic; -using System.Linq; -using Mono.AndroidTools; -using Mono.AndroidTools.Adb; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Diagnostics; - -namespace Xamarin.AndroidTools -{ - public class AndroidDeploySession - { - [Obsolete ("Use PackageApiLevel")] - public string PackageTargetApi { - get { return PackageApiLevel.ToString (); } - set { PackageApiLevel = int.Parse (value); } - } - - public int PackageApiLevel { get; set; } - public AndroidDevice Device { get; private set; } - public string PackageSupportedArchs { get; set; } - public IProgressNotifier ProgressReporter { get; set; } - - /// Whether the user wants unstripped binaries and BCL debug symbols - public bool ProvideFullDebugRuntime { get; set; } - - public bool UsesSharedRuntime { get; set; } - public bool PreserveUserData { get; set; } - - public bool ForcePackageInstall { get; set; } - public bool IsFastDev { get; set; } - public string PackageName { get; set; } - public string PackageFile { get; set; } - - public string AaptToolPath { get; set; } - - [Obsolete ("DeltaInstall support has been removed.", error: true)] - public bool AllowDeltaInstall { - get { throw new NotSupportedException ("DeltaInstall support has been removed."); } - set { throw new NotSupportedException ("DeltaInstall support has been removed."); } - } - - [Obsolete ("Activity execution at installation is no longer needed")] - public string Activity { get; set; } - - [Obsolete ("Use FastDevAssembliesProvider")] - public List Assemblies { get; set; } - - /// - /// Gets the name of the activity launched to create the FastDev assembly directory. - /// - [Obsolete ("Activity execution at installation is no longer needed")] - public Func FastDevActivityProvider { get; set; } - - /// - /// Gets the collection of assemblies to be deployed by FastDev. - /// - public Func> FastDevAssembliesProvider { get; set; } - - /// - /// Gets the collection of dexes to be deployed by FastDev. - /// - public Func> FastDevNativeLibrariesProvider { get; set; } - - /// - /// Gets the collection of dexes to be deployed by FastDev. - /// - public Func> FastDevDexesProvider { get; set; } - - /// - /// Gets the collection of packaged_resources to be deployed by FastDev. - /// - public Func> FastDevPackagedResourcesProvider { get; set; } - - /// - /// Gets the collection of resources to be deployed by FastDev. - /// - public Func> FastDevResourcesProvider { get; set; } - - /// - /// Gets the collection of typemap files to be deployed by FastDev. - /// - public Func> FastDevTypemapsProvider { get; set; } - - /// - /// If non-null, will block on this before deploying actual assemblies. Returns true if the package changed. - /// - public Task PackagingTask { get; set; } - - public bool External { get; private set; } - public string PackageRemotePath { get; private set; } - - private CancellationToken token; - private IList packages; - private string fastDevRemotePath; - - public AndroidDeploySession (AndroidDevice device, IList packages = null) - { - Device = device; - this.packages = packages; - - PreserveUserData = true; - } - - public async Task StartAsync (CancellationToken token) - { - await RunLoggedAsync (token).ConfigureAwait(false); - } - - async Task RunLoggedAsync (CancellationToken token) - { - try { - await RunAsync (token).ConfigureAwait(false); - } catch (Exception ex) { - // make sure we observe these exceptions otherwise they get logged as unhandled - // so do this before the cancellation check below - var aex = ex as AggregateException; - if (aex != null) { - ex = aex.Flatten ().InnerException; - } - - if (token.IsCancellationRequested) { - AndroidLogger.LogInfo ("Deployment cancelled"); - token.ThrowIfCancellationRequested (); - } - - if (ex is OperationCanceledException) - throw ex; - - AndroidLogger.LogError ("Deployment failed", ex); - if (ex is AndroidDeploymentException) { - throw; - } - - if (ex is DeviceNotFoundException || ex is DeviceDisconnectedException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.DeviceDisconnected, ex); - } - - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InternalError, ex); - } - } - - [Obsolete ("Use StartAsync")] - public bool Start (CancellationToken token) - { - try { - RunLoggedAsync (token).Wait (); - return true; - } catch (OperationCanceledException) { - // ignore - } catch (AndroidDeploymentException ex) { - string title, detail; - ex.GetNiceExplanation (out title, out detail); - ProgressReporter.ShowErrorDialog (title, detail, ex); - } - return false; - } - - async Task RunAsync (CancellationToken token) - { - this.token = token; - - await Device.EnsureProperties (token).ConfigureAwait(false); - - // Make sure the device arch is supported - if (!string.IsNullOrWhiteSpace (PackageSupportedArchs) && !Device.CanRunPackageArchitecture (PackageSupportedArchs)) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.ArchitectureNotSupported, this); - } - - if (UsesSharedRuntime && !Device.CanRunPackageArchitecture (MonoDroidSdk.SharedRuntimeAbis)) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.ArchitectureNotSupportedBySharedRuntime, this); - } - - string redirectStdio = Device.Properties.Get ("log.redirect-stdio"); - if (redirectStdio != null && string.Equals ("true", redirectStdio.Trim (), StringComparison.OrdinalIgnoreCase)) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.StdioRedirectionEnabled); - } - - if (packages == null) { - packages = await Device.GetPackagesAsync (PackageApiLevel, PackageName, ProvideFullDebugRuntime, token, ProgressReporter); - } - - if (packages == null) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FailedToGetPackageList); - } - - if (UsesSharedRuntime) { - await EnsureCorrectSharedRuntimes (); - } - - if (!UsesSharedRuntime && IsFastDev) { - if (string.IsNullOrWhiteSpace (Device.Properties.MonoLog)) { - await Device.SetProperty ("debug.mono.log", "gc"); - } - } - - //packaging can be done in parallel with installing the shared runtime, but need to block on it - //before installing the app package - if (PackagingTask != null && PackagingTask.Status != TaskStatus.RanToCompletion) { - ProgressReporter.BeginStep ("Waiting for packaging to complete"); - try { - await PackagingTask; - } catch (Exception ex) { - if (ex is AndroidDeploymentException || ex is OperationCanceledException) - throw; - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.PackagingFailed, ex); - } - ProgressReporter.EndStep (null); - } - - await InstallPackage (); - - if (IsFastDev) { - try { - await FastDevAsync (true); - } catch { - ShowProgressText ("Fast dev didn't succeed, trying another location...", token); - - await FastDevAsync (false); - } - } else { - var fastDevPath = await GetFastDevRemotePathAsync (true); - if (!string.IsNullOrEmpty (fastDevPath)) { - ShowProgressText ("Fast dev is disabled, removing fast dev directory: " + fastDevPath, token); - await Device.DeleteDirectory (fastDevPath, true, token); - } - fastDevPath = await GetFastDevRemotePathAsync (false); - if (!string.IsNullOrEmpty (fastDevPath)) { - ShowProgressText ("Fast dev is disabled, removing fast dev directory: " + fastDevPath, token); - await Device.DeleteDirectory (fastDevPath, true, token); - } - } - } - - async Task EnsureCorrectSharedRuntimes () - { - // See if there are any old runtimes we need to remove - await RemoveOldRuntimes (); - - // Install the shared runtime and platform - - try { - await CheckAndInstallSharedRuntimeAsync (); - } catch (Exception ex) { - if (ex is InsufficientSpaceException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InsufficientSpaceForRuntime, ex); - } - throw; - } - - try { - await InstallSharedPlatformAsync (); - } catch (Exception ex) { - if (ex is InsufficientSpaceException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InsufficientSpaceForPlatform, ex); - } - if (ex is SdkNotSupportedException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.SdkNotSupportedByDevice, ex); - } - throw; - } - } - - async Task RemoveOldRuntimes () - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return; - - // See if there are any old runtimes we need to remove - var old_runtimes = packages.GetOldRuntimesAndPlatforms (PackageApiLevel).ToList (); - if (old_runtimes.Count == 0) { - return; - } - - ProgressReporter.BeginStep ("Removing old runtimes"); - foreach (var runtime in old_runtimes) { - ShowProgressText (string.Format ("Removing old runtime: {0} [{1}]..", runtime.Name, runtime.Version), token); - await Device.UninstallPackage (runtime.Name, false, token); - packages.Remove (runtime); - } - ProgressReporter.EndStep (null); - } - - // Returns whether a new shared runtime was installed - private async Task CheckAndInstallSharedRuntimeAsync () - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return false; - - // See if the device already has the shared runtime - var needs_runtime = !packages.IsCurrentRuntimeInstalled (); - - // See if there is a runtime on the device that - // we cannot determine the version of - if (needs_runtime && packages.IsUnknownRuntimeInstalled ()) { - ProgressReporter.ShowErrorDialog ( - "Unknown Runtime", - "There is a shared runtime on the device whose version cannot be determined. " + - "A new runtime will not be deployed. If the runtime needs to be replaced, please manually " + - "remove it from the device."); - needs_runtime = false; - } - - // If needed, install the shared runtime - if (!needs_runtime) - return false; - - ProgressReporter.BeginStep ("Installing shared runtime"); - await Device.InstallSharedRuntimeAsync (ProvideFullDebugRuntime, token, ProgressReporter); - ProgressReporter.EndStep (null); - packages.Add (new AndroidInstalledPackage (AndroidPackageListExtensions.runtimeName, null, MonoDroidSdk.SharedRuntimeVersion)); - - return true; - } - - // Returns whether a new shared platform was installed - private async Task InstallSharedPlatformAsync () - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return false; - - // See if the device already has the shared platform - var needs_platform = !packages.IsCurrentPlatformInstalled (PackageApiLevel); - - // See if there is a runtime on the device that - // we cannot determine the version of - if (needs_platform && packages.IsUnknownPlatformInstalled (PackageApiLevel)) { - ProgressReporter.ShowErrorDialog ( - "Unknown Platform Runtime", - "There is a platform support runtime on the device whose version cannot be determined. " + - "A new platform support runtime will not be deployed. If the platform support runtime needs " + - "to be replaced, please manually remove it from the device."); - needs_platform = false; - } - - var platform_file = await PlatformPackage.GetPlatformPackagePathAsync (PackageApiLevel, AaptToolPath, ProgressReporter, token); - - var sharedRuntimePackage = string.Format (AndroidPackageListExtensions.platformName, PackageApiLevel); - var useXamarinPackage = sharedRuntimePackage; - var version = PlatformPackage.GetPlatformPackageVersion (PackageApiLevel, ref useXamarinPackage); - - // If needed, install the shared platform - if (needs_platform) { - ProgressReporter.BeginStep ("Installing platform framework"); - ShowProgressText (string.Format ("Installing the API {0} platform framework..", PackageApiLevel), token); - await Device.InstallSharedPlatformAsync (platform_file, PackageApiLevel, ProgressReporter.ReportProgress, token); - ProgressReporter.EndStep (null); - packages.Add (new AndroidInstalledPackage (sharedRuntimePackage, null, version)); - } - - if (useXamarinPackage.StartsWith ("Xamarin", StringComparison.OrdinalIgnoreCase)) { - ShowProgressText (string.Format ("Removing old {0} framework.", sharedRuntimePackage), token); - await Device.UninstallPackage (sharedRuntimePackage, false, token); - } - - return needs_platform; - } - - private void ShowProgressText (string text, CancellationToken token) - { - if (!token.IsCancellationRequested && ProgressReporter != null) - ProgressReporter.ReportMessage (text); - } - - async Task InstallPackage () - { - bool force = ForcePackageInstall || (PackagingTask != null && PackagingTask.Result); - - if (!force && packages.ContainsPackage (PackageName)) { - // If we didn't uninstall/reinstall, the app might already be running, - // which makes our attempts to start a new one to debug fail, so we - // are going to kill the already running copy. - AndroidLogger.LogDebug ("InstallPackage", "Checking whether app {0} is running", PackageName); - var pid = await Device.GetProcessId (PackageName, token); - if (pid == 0) { - AndroidLogger.LogDebug ("InstallPackage", "App was not running, skipping kill"); - return; - } - AndroidLogger.LogDebug ("InstallPackage", "Killing app"); - - ProgressReporter.BeginStep ("Terminating running application"); - ShowProgressText ("Terminating running application...", token); - await Device.KillProcessAndWaitForExit (PackageName, token); - ProgressReporter.EndStep (null); - return; - } - - token.ThrowIfCancellationRequested (); - - if (packages.ContainsPackage (PackageName)) { - ProgressReporter.BeginStep ("Removing previous version of application"); - ShowProgressText ("Removing previous version of application...", token); - - await Device.UninstallPackage (PackageName, PreserveUserData, token); - ProgressReporter.EndStep (null); - } - - token.ThrowIfCancellationRequested (); - - ProgressReporter.BeginStep ("Installing application on device"); - ShowProgressText ("Copying application to device...", token); - - try { - //TODO: check the package ABI by poking inside the apk, if PackageSupportedArchs was not set - await Device.PushAndInstallPackageAsync (new PushAndInstallCommand { - ApkFile = PackageFile, - PackageName = PackageName, - ReInstall = false, - NotifyProgress = ProgressReporter.ReportProgress, - }, token); - } catch (Exception exception) { - var ex = exception; - if (exception is AggregateException aex) { - ex = aex.Flatten ().InnerException; - } - if (ex is InsufficientSpaceException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.InsufficientSpaceForPackage, ex); - } - if (ex is SdkNotSupportedException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.SdkNotSupportedByDevice, ex); - } - if (ex is IncompatibleCpuAbiException) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.ArchitectureNotSupported, this, ex); - } - if (!ShouldThrowIfPackageInstallFailed (ex as PackageAlreadyExistsException, token)) { - ProgressReporter.EndStep (null); - return; - } - throw; - } - packages.Add (new AndroidInstalledPackage (PackageName, null)); - ProgressReporter.EndStep (null); - } - - bool ShouldThrowIfPackageInstallFailed (PackageAlreadyExistsException e, CancellationToken token) - { - if (e == null) - return true; - - int s = (e.PackageFile ?? "").LastIndexOf ('/'); - string apkBasename = s >= 0 ? e.PackageFile.Substring (s+1) : e.PackageFile; - - // If the runtime already exists, ignore the error - // Sometimes android doesn't report it's installed when it is :/ - if (apkBasename != Path.GetFileName (PackageFile)) - return false; - - // Oops; things have gotten wedged (stale/interrupted install?) - // The file we tried to upload already exists on the device! - // Delete and try again. - ShowProgressText (string.Format ("Package '{0}' already exists. Retrying...", PackageName), token); - try { - // NOTE We NEED to delete the cache data too other wise the install will fail. - Device.DeleteFile (e.PackageFile, true, token).Wait (token); - } catch { - // Ebil, yes, but... - } - ShowProgressText (string.Format ("Forcing complete uninstall of '{0}'...", PackageName), token); - Device.UninstallPackage (PackageName, false, token).Wait (token); - ShowProgressText (string.Format ("Installing '{0}'...", PackageName), token); - Device.PushAndInstallPackageAsync (new PushAndInstallCommand { - ApkFile = PackageFile, - ReInstall = false, - NotifyProgress = ProgressReporter.ReportProgress - }, token).Wait (token); - return false; - } - - async Task FastDevAsync (bool useExternal) - { - var dest = await GetFastDevRemotePathAsync (useExternal); - if (dest == null) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FailedToDetermineFastDevPath); - } - - ShowProgressText ("Using fast dev path: " + dest, token); - - await InstallAssemblies (dest, token); - // do not call these before InstallAssemblies otherwise InstallAssemblies will clean up resources. - await InstallNativeLibraries (dest, token); - await InstallDexes (dest, token); - await InstallPackagedResources (dest, token); - await InstallTypemaps (dest, token); - } - - async Task GetFastDevRemotePathAsync (bool useExternal) - { - ShowProgressText ("Getting installation path...", token); - - if (!string.IsNullOrEmpty (fastDevRemotePath) && !string.IsNullOrEmpty (PackageRemotePath)) { - ShowProgressText ($"Using cached value for installation path: {fastDevRemotePath}", token); - ShowProgressText ($"Using cached value for package remote path: {PackageRemotePath}", token); - return fastDevRemotePath; - } - - if (useExternal) { - External = true; - PackageRemotePath = await Device.GetPackageRemotePathAsync (PackageName, token); - -#pragma warning disable CS0618 // Type or member is obsolete - fastDevRemotePath = await Device.GetFastDevRemotePathExternalAsync (PackageName, token); -#pragma warning restore CS0618 - - return fastDevRemotePath; - } - - var result = await Device.GetFastDevRemotePathAsync (PackageName, token); - - PackageRemotePath = result.PackageRemotePath; - External = result.IsExternal; - fastDevRemotePath = result.Root; - - return fastDevRemotePath; - } - - static readonly HashSet AssemblyExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { - ".dll", - ".mdb", - }; - - async Task InstallAssemblies (string destinationPath, CancellationToken token) - { - token.ThrowIfCancellationRequested (); - - #pragma warning disable 618 - IEnumerable assemblies = FastDevAssembliesProvider != null? FastDevAssembliesProvider () : Assemblies; - #pragma warning restore 618 - - var progress = ProgressReporter; - var device = Device; - - var action = ForcePackageInstall? AdbSyncAction.Copy : AdbSyncAction.CopyIfNewer; - - var cultureDirectories = new Dictionary (); - var asmOverrideDir = new AdbSyncDirectory (Path.GetFileName (destinationPath), action, true); - - foreach (var a in assemblies) { - string culture, name; - AdbSyncDirectory dir; - if (TryGetSatelliteCultureAndFileName (a, out culture, out name) && culture != null) { - if (!cultureDirectories.TryGetValue (culture, out dir)) { - cultureDirectories [culture] = dir = new AdbSyncDirectory (culture, action, true); - asmOverrideDir.Add (dir); - } - } else { - name = Path.GetFileName (a); - dir = asmOverrideDir; - } - dir.Add (new AdbSyncFile (a, name, action)); - } - - var files = await device.ListFilesAsync (destinationPath.Replace ('\\', '/'), token); - - foreach (var remoteFile in files.Where (x => AssemblyExtensions.Contains (Path.GetExtension (x)))) { - if (!assemblies.Any (x => Path.GetFileName (x) == Path.GetFileName (remoteFile))) { - asmOverrideDir.Add (AdbSyncItem.Delete (remoteFile)); - } - } - - ShowProgressText ("Synchronizing assemblies...", token); - ProgressReporter.BeginStep ("Synchronizing assemblies"); - - try { - if (destinationPath.StartsWith ("/data", StringComparison.Ordinal)) { - WaitForRemoteDirCreation (destinationPath, token); - } - - token.ThrowIfCancellationRequested (); - - var t = device.PushSyncItems (asmOverrideDir, - Path.GetDirectoryName (destinationPath).Replace ('\\', '/'), - new AdbSyncClient.PushOptions () { - DryRun = false, - RemoveUnknown = true, - CheckTimestamps = true, - NotifySync = (a) => AndroidLogger.LogDebug ("NotifySync", "{0} {1} {2} {3}", a.Kind, a.LocalPath, a.RemotePath, a.Size), - NotifyPhase = (b) => AndroidLogger.LogDebug ("NotifyPhase", "{0}", b), - NotifyProgress = progress.ReportProgress, - RemoveBeforeCopy = false, - }, token); - await t; - } catch (Exception ex) { - if (ex is DeviceNotFoundException - || ex is DeviceDisconnectedException - || ex is AndroidDeploymentException - || ex is OperationCanceledException) - throw; - - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FailedToSynchronizeFastDevAssemblies, ex); - } finally { - ProgressReporter.EndStep (null); - } - } - - - Task InstallNativeLibraries (string destinationPath, CancellationToken token) - { - // native lib overrides are copied to .__override__ like assemblies, so we follow that. - return InstallFastDevFiles (destinationPath, FastDevNativeLibrariesProvider, "native libs", new string [] { ".__override__", "lib" }, removeUnknown: true, token: token); - } - - Task InstallDexes (string destinationPath, CancellationToken token) - { - return InstallFastDevFiles (destinationPath, FastDevDexesProvider, "dexes", new string [] { ".__override__", "dexes" }, removeUnknown: true, token: token); - } - - Task InstallPackagedResources (string destinationPath, CancellationToken token) - { - return InstallFastDevFiles (destinationPath, FastDevPackagedResourcesProvider, "resources", new string [] { ".__override__", "packaged" }, removeUnknown: true, token: token); - } - - Task InstallTypemaps (string destinationPath, CancellationToken token) - { - return InstallFastDevFiles (destinationPath, FastDevTypemapsProvider, "typemaps", new string [] { ".__override__", "typemaps" }, removeUnknown: true, token: token); - } - - async Task InstallFastDevFiles (string destinationPath, Func> filesProvider, string fileKind, string [] syncDirs, bool removeUnknown, CancellationToken token) - { - if (filesProvider == null) { - ShowProgressText ($"Skipping {fileKind}, not configured...", token); - return; - } - - token.ThrowIfCancellationRequested (); - - var progress = ProgressReporter; - var device = Device; - - var action = ForcePackageInstall? AdbSyncAction.Copy : AdbSyncAction.CopyIfNewer; - - AdbSyncDirectory overrideDir = null, destinationDir = null; - foreach (var syncDir in syncDirs.Reverse ()) { - overrideDir = new AdbSyncDirectory (syncDir, action, removeUnknown, Enumerable.Repeat (overrideDir, overrideDir == null ? 0 : 1)); - destinationDir = destinationDir ?? overrideDir; - } - - var files = await device.ListFilesAsync (Path.Combine (destinationPath, destinationDir.Name).Replace ('\\', '/'), token); - - var localFiles = filesProvider (); - foreach (var remoteFile in files) { - if (string.IsNullOrEmpty (remoteFile)) - continue; - if (!localFiles.Any (x => Path.GetFileName (x) == Path.GetFileName (remoteFile))) - destinationDir.Add (AdbSyncItem.Delete (remoteFile)); - } - - foreach (var a in localFiles) { - AdbSyncDirectory dir; - string name = Path.GetFileName (a); - dir = destinationDir; - dir.Add (new AdbSyncFile (a, name, action)); - } - - ShowProgressText (string.Format ("Synchronizing {0}...", fileKind), token); - ProgressReporter.BeginStep (string.Format ("Synchronizing {0}", fileKind)); - - try { - if (destinationPath.StartsWith ("/data", StringComparison.Ordinal)) { - WaitForRemoteDirCreation (destinationPath, token); - } - - token.ThrowIfCancellationRequested (); - - var t = device.PushSyncItems (overrideDir, - Path.GetDirectoryName (destinationPath).Replace ('\\', '/'), - new AdbSyncClient.PushOptions () { - DryRun = false, - RemoveUnknown = true, - RemoveBeforeCopy = false, - CheckTimestamps = true, - NotifySync = (a) => AndroidLogger.LogDebug ("NotifySync", "{0} {1} {2} {3}", a.Kind, a.LocalPath, a.RemotePath, a.Size), - NotifyPhase = (b) => AndroidLogger.LogDebug ("NotifyPhase", "{0}", b), - NotifyProgress = progress.ReportProgress - }, token); - - await t; - } catch (Exception ex) { - if (ex is DeviceNotFoundException - || ex is DeviceDisconnectedException - || ex is AndroidDeploymentException - || ex is OperationCanceledException) - throw; - - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FailedToSynchronizeFastDevResources, ex); - } finally { - ProgressReporter.EndStep (null); - } - } - - void WaitForRemoteDirCreation (string destinationPath, CancellationToken token) - { - var device = Device; - var packageName = PackageName; - int count = 0; - var wait = new ManualResetEvent (false); - // the logic here is: - // check if the target directory exists - // if it isn't, don't try to push, start the app to get them created - // directories exist, push assemblies - // - // Note: checking if the directory exists before trying to push - // is important because an emulator or rooted device may allow the - // push to succeed, but it will create everything with root permissions. - // If the app then tries to do anything with the files in the private dir, - // things will fail. Directories must be created by monodroid for permissions - // to be set correctly. - // - // All this is important for devices running Android 3.0+. Before that, just - // killing the process was enough for monodroid to run and create the directories, - // so by the time we reach this point, the directory already exists. - - var fi = device.GetRemoteFileInfo (destinationPath, token).Result; - AndroidLogger.LogDebug ("Stat", "FileInfo for {0}: {1}", destinationPath, fi != null ? fi.GetSymbolicMode () : "NONE"); - - if (fi == null || fi.IsFileType (AdbFileMode.S_IFREG)) { - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FastDevFileConflict); - } - - if (fi.IsFileType (AdbFileMode.S_IFDIR)) { - return; - } - - // If the directory doesn't exist, we're going to try starting the app to create it. - // on Android 4.2+ broadcasts don't return until long after they actually hit the app, so - // we don't block on the broastcast. Instead, we poll for the directory creation and app termination. - device.SendSeppukuBroadcast (packageName, token); - do { - if (!fi.IsFileType (AdbFileMode.S_IFDIR)) { - fi = device.GetRemoteFileInfo (destinationPath, token).Result; - AndroidLogger.LogDebug ("Stat", "{0} FileInfo for {1} : {2}", count, destinationPath, fi.GetSymbolicMode ()); - } - - // If directory has been created, still need to make sure the process exited, - // so we don't leave stray suicidal processes. - if (fi.IsFileType (AdbFileMode.S_IFDIR)) { - - // If targeting Android 4.0+, we have a reliable hard kill. - if (device.Properties.BuildVersionSdk >= 14) { - device.KillProcess (packageName, token).Wait (); - return; - } - - // Else, just poll until it exits. - if (device.GetProcessId (packageName, token).Result == 0) { - return; - } - } - - token.ThrowIfCancellationRequested (); - - // The ResetEvent objects are handy for suspending operations for a bit without the - // overhead of a Thread.Sleep - wait.WaitOne (200); - } while (++count <= 5*10); // this ends up waiting in chunks for about 10 seconds at the most, to give monodroid time to run. - - throw new AndroidDeploymentException (AndroidDeploymentFailureReason.FastDevDirectoryCreationFailed); - } - - // culture match courtesy: http://stackoverflow.com/a/3962783/83444 - static readonly Regex SatelliteChecker = new Regex ( - Regex.Escape (Path.DirectorySeparatorChar.ToString ()) + - "(?[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*)" + - Regex.Escape (Path.DirectorySeparatorChar.ToString ()) + - string.Format ("(?[^{0}]+.resources.dll)$", Regex.Escape (Path.DirectorySeparatorChar.ToString ()))); - - public static bool TryGetSatelliteCultureAndFileName (string assemblyPath, out string culture, out string fileName) - { - culture = fileName = null; - - var m = SatelliteChecker.Match (assemblyPath); - if (!m.Success) - return false; - - culture = m.Groups ["culture"].Value; - fileName = m.Groups ["file"].Value; - return true; - } - } - - public class AndroidDeployRuntimeSession - { - private CancellationToken token; - private IList packages; - - public IProgressNotifier ProgressReporter { get; set; } - - public int PackageApiLevel { get; set; } - public AndroidDevice Device { get; private set; } - - public bool UsesSharedRuntime { get; set; } - - /// Whether the user wants unstripped binaries and BCL debug symbols - public bool ProvideFullDebugRuntime { get; set; } - - public string AaptToolPath { get; set; } - - public string PackageSupportedArchs { get; set; } - - public string PackageName { get; set; } - - - public AndroidDeployRuntimeSession(AndroidDevice device, IList packages = null) - { - Device = device; - this.packages = packages; - } - - public async Task StartAsync(CancellationToken token) - { - try - { - await RunAsync(token).ConfigureAwait(false); - } - catch (Exception ex) - { - // make sure we observe these exceptions otherwise they get logged as unhandled - // so do this before the cancellation check below - var aex = ex as AggregateException; - if (aex != null) - { - ex = aex.Flatten().InnerException; - } - - if (token.IsCancellationRequested) - { - AndroidLogger.LogInfo("Deployment cancelled"); - token.ThrowIfCancellationRequested(); - } - - if (ex is OperationCanceledException) - throw ex; - - AndroidLogger.LogError("Deployment failed", ex); - if (ex is AndroidDeploymentException) - { - throw; - } - - if (ex is DeviceNotFoundException || ex is DeviceDisconnectedException) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.DeviceDisconnected, ex); - } - - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.InternalError, ex); - } - } - - async Task RunAsync(CancellationToken token) - { - if (!UsesSharedRuntime) - { - return; - } - - this.token = token; - - await Device.EnsureProperties(token).ConfigureAwait(false); - - // Make sure the device arch is supported - if (!string.IsNullOrWhiteSpace(PackageSupportedArchs) && !Device.CanRunPackageArchitecture(PackageSupportedArchs)) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.ArchitectureNotSupported); - } - - if (UsesSharedRuntime && !Device.CanRunPackageArchitecture(MonoDroidSdk.SharedRuntimeAbis)) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.ArchitectureNotSupportedBySharedRuntime); - } - - string redirectStdio = Device.Properties.Get("log.redirect-stdio"); - if (redirectStdio != null && string.Equals("true", redirectStdio.Trim(), StringComparison.OrdinalIgnoreCase)) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.StdioRedirectionEnabled); - } - - if (packages == null) - { - packages = await Device.GetPackagesAsync(PackageApiLevel, PackageName, ProvideFullDebugRuntime, token, ProgressReporter); - } - - if (packages == null) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.FailedToGetPackageList); - } - - if (UsesSharedRuntime) - { - await EnsureCorrectSharedRuntimes().ConfigureAwait(false); - } - } - - async Task EnsureCorrectSharedRuntimes() - { - // See if there are any old runtimes we need to remove - await RemoveOldRuntimes().ConfigureAwait(false); - - // Install the shared runtime and platform - - try - { - await CheckAndInstallSharedRuntimeAsync().ConfigureAwait(false); - } - catch (Exception ex) - { - if (ex is InsufficientSpaceException) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.InsufficientSpaceForRuntime, ex); - } - throw; - } - - try - { - await InstallSharedPlatformAsync().ConfigureAwait(false); - } - catch (Exception ex) - { - if (ex is InsufficientSpaceException) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.InsufficientSpaceForPlatform, ex); - } - if (ex is SdkNotSupportedException) - { - throw new AndroidDeploymentException(AndroidDeploymentFailureReason.SdkNotSupportedByDevice, ex); - } - throw; - } - } - - async Task RemoveOldRuntimes() - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return; - - // See if there are any old runtimes we need to remove - var old_runtimes = packages.GetOldRuntimesAndPlatforms(PackageApiLevel).ToList(); - if (old_runtimes.Count == 0) - { - return; - } - - ProgressReporter.BeginStep("Removing old runtimes"); - foreach (var runtime in old_runtimes) - { - ShowProgressText(string.Format("Removing old runtime: {0} [{1}]..", runtime.Name, runtime.Version), token); - await Device.UninstallPackage(runtime.Name, false, token); - packages.Remove(runtime); - } - ProgressReporter.EndStep(null); - } - - // Returns whether a new shared runtime was installed - private async Task CheckAndInstallSharedRuntimeAsync() - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return false; - - // See if the device already has the shared runtime - var needs_runtime = !packages.IsCurrentRuntimeInstalled(); - - // See if there is a runtime on the device that - // we cannot determine the version of - if (needs_runtime && packages.IsUnknownRuntimeInstalled()) - { - ProgressReporter.ShowErrorDialog( - "Unknown Runtime", - "There is a shared runtime on the device whose version cannot be determined. " + - "A new runtime will not be deployed. If the runtime needs to be replaced, please manually " + - "remove it from the device."); - needs_runtime = false; - } - - // If needed, install the shared runtime - if (!needs_runtime) - return false; - - ProgressReporter.BeginStep("Installing shared runtime"); - await Device.InstallSharedRuntimeAsync(ProvideFullDebugRuntime, token, ProgressReporter); - ProgressReporter.EndStep(null); - - return true; - } - - // Returns whether a new shared platform was installed - private async Task InstallSharedPlatformAsync() - { - // See if we were asked to cancel - if (token.IsCancellationRequested) - return false; - - // See if the device already has the shared platform - var needs_platform = !packages.IsCurrentPlatformInstalled(PackageApiLevel); - - // See if there is a runtime on the device that - // we cannot determine the version of - if (needs_platform && packages.IsUnknownPlatformInstalled(PackageApiLevel)) - { - ProgressReporter.ShowErrorDialog( - "Unknown Platform Runtime", - "There is a platform support runtime on the device whose version cannot be determined. " + - "A new platform support runtime will not be deployed. If the platform support runtime needs " + - "to be replaced, please manually remove it from the device."); - needs_platform = false; - } - - var platform_file = await PlatformPackage.GetPlatformPackagePathAsync (PackageApiLevel, AaptToolPath, ProgressReporter, token); - - // If needed, install the shared platform - if (needs_platform) - { - ProgressReporter.BeginStep("Installing platform framework"); - ShowProgressText(string.Format("Installing the API {0} platform framework..", PackageApiLevel), token); - await Device.InstallSharedPlatformAsync(platform_file, PackageApiLevel, ProgressReporter.ReportProgress, token); - ProgressReporter.EndStep(null); - } - - var sharedRuntimePackage = string.Format(AndroidPackageListExtensions.platformName, PackageApiLevel); - var useXamarinPackage = sharedRuntimePackage; - PlatformPackage.GetPlatformPackageVersion(PackageApiLevel, ref useXamarinPackage); - if (useXamarinPackage.StartsWith("Xamarin", StringComparison.OrdinalIgnoreCase)) - { - ShowProgressText(string.Format("Removing old {0} framework.", sharedRuntimePackage), token); - await Device.UninstallPackage(sharedRuntimePackage, false, token).ConfigureAwait(false); - } - - return needs_platform; - } - - private void ShowProgressText(string text, CancellationToken token) - { - if (!token.IsCancellationRequested && ProgressReporter != null) - ProgressReporter.ReportMessage(text); - } - } - -} diff --git a/src/Xamarin.AndroidTools/Sessions/AndroidDeploymentException.cs b/src/Xamarin.AndroidTools/Sessions/AndroidDeploymentException.cs deleted file mode 100644 index 8f6d06cc3bb..00000000000 --- a/src/Xamarin.AndroidTools/Sessions/AndroidDeploymentException.cs +++ /dev/null @@ -1,175 +0,0 @@ -// -// AndroidDeploymentException.cs -// -// Authors: -// Michael Hutchinson -// -// Copyright 2013 Xamarin Inc. All rights reserved. -// - -using System; - -namespace Xamarin.AndroidTools -{ - public class AndroidDeploymentException : Exception - { - AndroidDeploySession session; - - public AndroidDeploymentFailureReason Reason { - get; private set; - } - - public AndroidDeploymentException (AndroidDeploymentFailureReason reason) : base (reason.ToString ()) - { - Reason = reason; - } - - public AndroidDeploymentException (AndroidDeploymentFailureReason reason, AndroidDeploySession session) - : base (reason.ToString ()) - { - Reason = reason; - this.session = session; - } - - public AndroidDeploymentException (AndroidDeploymentFailureReason reason, Exception inner) - : base (reason.ToString (), InnerExceptionFromAggregate (inner)) - { - Reason = reason; - } - - public AndroidDeploymentException (AndroidDeploymentFailureReason reason, AndroidDeploySession session, Exception inner) - : base (reason.ToString (), InnerExceptionFromAggregate (inner)) - { - Reason = reason; - this.session = session; - } - - static Exception InnerExceptionFromAggregate (Exception ex) - { - if (ex is AggregateException) - return ((AggregateException)ex).Flatten ().InnerException; - return ex; - } - - public void GetNiceExplanation (out string title, out string detail) - { - switch (Reason) { - case AndroidDeploymentFailureReason.DeviceDisconnected: - title = "Device disconnected."; - detail = "The device was disconnected. Please reconnect it and try again."; - break; - case AndroidDeploymentFailureReason.ArchitectureNotSupported: - title = "Architecture not supported."; - detail = string.Format ( - "The package does not support the device architecture ({0}). You can change the supported " + - "architectures in the Android Build section of the Project Options.", - session.Device.SupportedArchitecturesFormatted () - ); - break; - case AndroidDeploymentFailureReason.FailedToGetPackageList: - title = "Package list not found."; - detail = "The package list could not be read from the device."; - break; - case AndroidDeploymentFailureReason.PackagingFailed: - title = "Packaging failed."; - detail = "Deployment was cancelled as the package failed to build."; - break; - case AndroidDeploymentFailureReason.InsufficientSpaceForRuntime: - title = "Insufficient space on device."; - detail = - "Deployment failed because there was insufficient space on the device to install the shared " + - "Mono runtime package. Please make space and try again."; - break; - case AndroidDeploymentFailureReason.InsufficientSpaceForPlatform: - title = "Insufficient space on device."; - detail = - "Deployment failed because there was insufficient space on the device to install the shared " + - "Mono platform package. Please make space and try again."; - break; - case AndroidDeploymentFailureReason.SdkNotSupportedByDevice: - title = "Minimum Android version not supported by device."; - detail = "Deployment failed because the device does not support the package's minimum Android version. " + - "You can change the minimum Android version in the Android Application section of the " + - "Project Options."; - break; - case AndroidDeploymentFailureReason.InsufficientSpaceForPackage: - title = "Insufficient space on device."; - detail = - "Deployment failed because there was insufficient space on the device to install the package. " + - "Please make space and try again."; - break; - case AndroidDeploymentFailureReason.FailedToDetermineFastDevPath: - title = "FastDev path not found."; - detail = "Deployment failed because the FastDev assembly installation path could not be determined."; - break; - case AndroidDeploymentFailureReason.FailedToSynchronizeFastDevAssemblies: - title = "Assembly synchronization error."; - detail = "Deployment failed due to an error in FastDev assembly synchronization."; - break; - case AndroidDeploymentFailureReason.FailedToSynchronizeFastDevResources: - title = "Resource synchronization error."; - detail = "Deployment failed due to an error in FastDev resource synchronization."; - break; - case AndroidDeploymentFailureReason.FastDevActivityNotFound: - title = "FastDev activity not found."; - detail = "Deployment failed because a FastDev activity was not found in the package."; - break; - case AndroidDeploymentFailureReason.FastDevFileConflict: - title = "FastDev file conflict."; - detail = - "Deployment failed because a FastDev assembly conflicted with an existing file. Try " + - "manually removing the application from the device before redeploying."; - break; - case AndroidDeploymentFailureReason.FastDevDirectoryCreationFailed: - title = "FastDev directory creation failed."; - detail = "Deployment failed because the FastDev assembly directory could not be created."; - break; - case AndroidDeploymentFailureReason.ArchitectureNotSupportedBySharedRuntime: - title = "Architecture not supported."; - detail = string.Format ( - "The shared runtime package does not support the device architecture ({0}).", - session.Device.SupportedArchitecturesFormatted () - ); - break; - case AndroidDeploymentFailureReason.StdioRedirectionEnabled: - title = "Android stdio redirection is enabled."; - detail = - "The log.redirect-stdio system property is set to 'true'. " + - "This is not supported; it must be set to 'false'. " + - "See: http://developer.android.com/tools/debugging/debugging-log.html#viewingStd"; - break; - default: - case AndroidDeploymentFailureReason.InternalError: - title = "Internal error."; - if (InnerException != null) { - detail = "Deployment failed because of an internal error: " + InnerException.Message; - } else { - detail = "Deployment failed because of an internal error."; - } - break; - } - } - } - - public enum AndroidDeploymentFailureReason - { - DeviceDisconnected, - InternalError, - ArchitectureNotSupported, - FailedToGetPackageList, - PackagingFailed, - InsufficientSpaceForRuntime, - InsufficientSpaceForPlatform, - SdkNotSupportedByDevice, - InsufficientSpaceForPackage, - FailedToDetermineFastDevPath, - FailedToSynchronizeFastDevAssemblies, - FailedToSynchronizeFastDevResources, - FastDevActivityNotFound, - FastDevFileConflict, - FastDevDirectoryCreationFailed, - ArchitectureNotSupportedBySharedRuntime, - StdioRedirectionEnabled, - RequiresUninstall, - } -}