From f3b33a3fae3a4d6b7543afbf8e4351c414ce2178 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Tue, 9 Jun 2026 06:18:49 -0700 Subject: [PATCH 1/4] Localized file check-in by OneLocBuild Task (#11610) Build definition ID 17928: Build ID 14320800 --- src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx | 3 +++ .../Properties/Resources.pt-BR.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx | 3 +++ src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx | 3 +++ .../Properties/Resources.zh-Hans.resx | 3 +++ .../Properties/Resources.zh-Hant.resx | 3 +++ 13 files changed, 39 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx index c37bace3e10..69781dae0ac 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.cs.resx @@ -142,6 +142,9 @@ Tato chyba je pravděpodobně způsobená problémem se souborem AndroidManifest Neplatný název souboru: Názvy souborů nemůžou používat vyhrazená slova Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Nepovedlo se určit úroveň rozhraní API pro $(TargetFrameworkVersion) {0}. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx index 867a315d148..ae6a89c1fb1 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.de.resx @@ -142,6 +142,9 @@ Dieser Fehler wird wahrscheinlich durch ein Problem mit der Datei "AndroidManife Ungültiger Dateiname: Dateinamen können keine reservierten Java-Wörter verwenden. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Die API-Ebene für $(TargetFrameworkVersion) {0} konnte nicht bestimmt werden. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx index bb6be32179d..530e86e2326 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.es.resx @@ -142,6 +142,9 @@ Este error se debe probablemente a un problema con el archivo AndroidManifest.xm Nombre de archivo no válido: los nombres de archivo no pueden usar palabras reservadas de Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + No se pudo determinar el nivel de API para $(TargetFrameworkVersion) de "{0}". The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx index dbb42f34c46..d9d8ae5ab95 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.fr.resx @@ -142,6 +142,9 @@ Cette erreur est probablement due à un problème avec le fichier AndroidManifes Nom de fichier non valide : les noms de fichiers ne peuvent pas utiliser de mots réservés Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Impossible de déterminer le niveau d'API pour $(TargetFrameworkVersion) '{0}'. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx index 9c01ece54d2..215721367e4 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.it.resx @@ -142,6 +142,9 @@ Questo errore è probabilmente causato da un problema con il file di AndroidMani Nome file non valido: i nomi di file non possono usare parole riservate in Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Non è stato possibile determinare il livello API per $(TargetFrameworkVersion) di '{0}'. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx index dc4d3f5fcf7..d2f0c12e195 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ja.resx @@ -142,6 +142,9 @@ 無効なファイル名: ファイル名には Java の予約語を使用できません。 + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + '{0}' の $(TargetFrameworkVersion) の API レベルを特定できませんでした。 The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx index acca3ecb3f8..abaa97e297a 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ko.resx @@ -142,6 +142,9 @@ 잘못된 파일 이름: 파일 이름은 Java 예약어를 사용할 수 없습니다. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + '{0}'의 $(TargetFrameworkVersion)에 대한 API 레벨을 확인할 수 없습니다. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx index 21e5facf5d7..0202e7740c3 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pl.resx @@ -142,6 +142,9 @@ Ten błąd jest prawdopodobnie spowodowany problemem dotyczącym pliku AndroidMa Nieprawidłowa nazwa pliku: nazwy plików nie mogą używać słów zastrzeżonych języka Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Nie można określić poziomu interfejsu API dla parametru $(TargetFrameworkVersion) elementu „{0}”. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx index a530143bb84..3b7a33c22e1 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.pt-BR.resx @@ -142,6 +142,9 @@ Provavelmente esse erro foi causado por um problema com o arquivo AndroidManifes Nome de arquivo inválido: os nomes de arquivo não podem usar palavras reservadas para Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Não foi possível determinar o nível da API da $(TargetFrameworkVersion) de '{0}'. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx index c918fb73088..df89d66680d 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.ru.resx @@ -142,6 +142,9 @@ Недопустимое имя файла: в именах файлов не могут использоваться зарезервированные слова Java. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + Не удалось определить уровень API для $(TargetFrameworkVersion) "{0}". The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx index d196203df5a..e27978fba0c 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.tr.resx @@ -142,6 +142,9 @@ Bu hata, büyük olasılıkla AndroidManifest.xml dosyasında veya kaynak kod do Geçersiz dosya adı: Dosya adlarında Java ayrılmış sözcükleri kullanılamaz. + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + '{0}' değerine sahip $(TargetFrameworkVersion) için API düzeyi belirlenemedi. The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx index dd143210e19..e5489cbd49b 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hans.resx @@ -142,6 +142,9 @@ 文件名无效: 文件名不能使用 Java 保留字。 + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + 无法确定 $(TargetFrameworkVersion) "{0}" 的 API 级别。 The following are literal names and should not be translated: $(TargetFrameworkVersion) diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx index 69cd92b001d..ebeb93abdad 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.zh-Hant.resx @@ -142,6 +142,9 @@ 檔案名稱無效: 檔案名稱不能使用 JAVA 保留字。 + + Assembly '{0}' contains reference to obsolete attribute 'Android.Runtime.PreserveAttribute'. Members with this attribute may be trimmed. Please use System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute instead + 無法判斷 '{0}' 的 $(TargetFrameworkVersion) API 層級。 The following are literal names and should not be translated: $(TargetFrameworkVersion) From 63951b8b04bfbb80c2250f9dd58ce87e9aee9097 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 9 Jun 2026 11:06:38 -0500 Subject: [PATCH 2/4] [xaprepare] Check in `Mono.Android.Apis.projitems`, remove generator (#11608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Mono.Android.Apis.projitems` was previously generated at prepare time by `GeneratedMonoAndroidProjitemsFile` (in xaprepare) from the `BuildAndroidPlatforms.AllPlatforms` list. Both had exactly one consumer (each other), so check in the projitems as a source file and drop the generator plumbing. Changes: * Add `src/Mono.Android/Mono.Android.Apis.projitems` — content matches the generator's output byte-for-byte; only the header comment now points at `Documentation/workflow/HowToAddNewApiLevel.md` instead of declaring "GENERATED FILE". * Repoint the 4 importers (`Mono.Android.targets` x2, `create-installers.targets`, `create-android-api.csproj`, `Xamarin.Android.Build.Tasks.targets`) at the new path and drop the `Condition="Exists(...)"` guards — the file is always present now. * Delete `GeneratedMonoAndroidProjitemsFile.cs` and `AndroidPlatform.cs` (whole files — `AndroidPlatform` class and `AndroidPlatformExtensions` both become unused). * Drop the `new GeneratedMonoAndroidProjitemsFile()` registration in `Step_GenerateFiles.cs`. * Remove `BuildAndroidPlatforms.AllPlatforms` and the now-unused `using System.Collections.Generic;`. Keep the NDK constants (`AndroidNdkVersion`, `AndroidNdkPkgRevision`, `NdkMinimumAPI`, `NdkMinimumAPILegacy32`) — they still have consumers. * Update `Documentation/workflow/HowToAddNewApiLevel.md` with the XML-based instructions for adding a new API level. * Narrow the `build-tools/xaprepare/README.md` blurb for `BuildAndroidPlatforms.cs` to NDK metadata. * Fix stale doc comment in `GenerateSupportedPlatforms.cs` pointing at the old generated path. Continues the xaprepare cleanup started in PRs #11568 and #11580. ### [docs] Fix Stable=True/False contradiction in HowToAddNewApiLevel The added prose said Stable should be False for preview API levels, but the example (CANARY) sets True, and every entry in Mono.Android.Apis.projitems is True. Rewrite the prose to match the data and briefly explain what setting Stable=False would actually do (excludes entry from default stable framework selection). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Documentation/workflow/HowToAddNewApiLevel.md | 25 +- .../GenerateSupportedPlatforms.cs | 2 +- .../create-android-api.csproj | 2 +- .../installers/create-installers.targets | 2 +- build-tools/xaprepare/README.md | 5 +- .../xaprepare/Application/AndroidPlatform.cs | 53 ---- .../GeneratedMonoAndroidProjitemsFile.cs | 59 ----- .../ConfigAndData/BuildAndroidPlatforms.cs | 43 --- .../xaprepare/Steps/Step_GenerateFiles.cs | 4 +- src/Mono.Android/Mono.Android.Apis.projitems | 244 ++++++++++++++++++ src/Mono.Android/Mono.Android.targets | 4 +- .../Xamarin.Android.Build.Tasks.targets | 2 +- 12 files changed, 271 insertions(+), 174 deletions(-) delete mode 100644 build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs delete mode 100644 build-tools/xaprepare/xaprepare/Application/GeneratedMonoAndroidProjitemsFile.cs create mode 100644 src/Mono.Android/Mono.Android.Apis.projitems diff --git a/Documentation/workflow/HowToAddNewApiLevel.md b/Documentation/workflow/HowToAddNewApiLevel.md index 72764ffba8f..679120aed2c 100644 --- a/Documentation/workflow/HowToAddNewApiLevel.md +++ b/Documentation/workflow/HowToAddNewApiLevel.md @@ -111,17 +111,28 @@ For the new API level, you need: Then update the following files: - - Add new `AndroidPlatform` value to - [`/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs`](../../build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs): - - ```csharp - new AndroidPlatform (apiName: "CANARY", apiLevel: new Version (36, 1), platformID: "CANARY", include: "v16.0", framework: "v16.1", stable: false), + - Add a new `` entry to + [`/src/Mono.Android/Mono.Android.Apis.projitems`](../../src/Mono.Android/Mono.Android.Apis.projitems): + ```xml + + CANARY + 36 + 36.1 + 36.1 + True + ``` - TODO: what should be done for the "mid-year" updates, as is the case for API-CANARY? + `Include` is the binding framework version (e.g. `v16.1`). `Level` is the + integer API level (`Major` of `VersionCodeFull`). `Id` is the platform ID + used to locate `android-$(Id)` directories under the Android SDK. `Stable` + should be `True`; every entry currently in the projitems uses `True`, + including preview codenames like CANARY. (Setting it to `False` would + exclude the entry from being picked as the default stable framework + version by some build-time selection logic.) - What are `include` and `framework` used for? + TODO: what should be done for the "mid-year" updates, as is the case for API-CANARY? - Add new level to [`/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs`](../../build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs): diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs index 80947479a24..72ec0abe662 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs @@ -17,7 +17,7 @@ namespace Xamarin.Android.Tools.BootstrapTasks public class GenerateSupportedPlatforms : Task { /// - /// @(AndroidApiInfo) from .\bin\Build$(Configuration)\Mono.Android.Apis.projitems + /// @(AndroidApiInfo) from src\Mono.Android\Mono.Android.Apis.projitems /// [Required] public ITaskItem [] AndroidApiInfo { get; set; } = []; diff --git a/build-tools/create-android-api/create-android-api.csproj b/build-tools/create-android-api/create-android-api.csproj index abdc341b18d..de85695be10 100644 --- a/build-tools/create-android-api/create-android-api.csproj +++ b/build-tools/create-android-api/create-android-api.csproj @@ -6,7 +6,7 @@ - + diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index 44504f25ef4..79ad2b47289 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -3,7 +3,7 @@ - + $(XAInstallPrefix)xbuild-frameworks\MonoAndroid\ diff --git a/build-tools/xaprepare/README.md b/build-tools/xaprepare/README.md index c34a67425bc..1e792f7ab88 100644 --- a/build-tools/xaprepare/README.md +++ b/build-tools/xaprepare/README.md @@ -101,9 +101,8 @@ The files mentioned above are found in the [ConfigAndData](xaprepare/ConfigAndDa helper methods used throughout the preparation utility code. **Be very careful** when modifying the names there as it may break the build! - [BuildAndroidPlatforms.cs](xaprepare/ConfigAndData/BuildAndroidPlatforms.cs) - Modified whenever a new Android platform is added, this file names all of the Android API levels along with platform/API - identifiers and .NET for Android framework names corresponding to specific API levels. The file also contains specification - of minimum NDK API levels used for all the Android device targets. + Contains the NDK release/revision constants and the minimum NDK API levels used for all the Android device targets. + The Android API level list itself lives in [`/src/Mono.Android/Mono.Android.Apis.projitems`](../../src/Mono.Android/Mono.Android.Apis.projitems). - [CommonLicenses.cs](xaprepare/ConfigAndData/CommonLicenses.cs) A file with constants containing paths to licenses commonly used by software .NET for Android uses. The licenses are used when generating Third Party Notices. diff --git a/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs b/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs deleted file mode 100644 index 6d395c53c16..00000000000 --- a/build-tools/xaprepare/xaprepare/Application/AndroidPlatform.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Xamarin.Android.Prepare -{ - class AndroidPlatform - { - public string? ApiName { get; } - public Version ApiLevel { get; } - public string PlatformID { get; } - public string Framework { get; } - public bool Stable { get; } - public bool Supported { get; } - public string? Include { get; } - - public AndroidPlatform (uint apiLevel, string platformID, string? framework = null, bool stable = true, string? apiName = null, string? include = null) - : this (new Version (checked ((int) apiLevel), 0), platformID, framework, stable, apiName, include) - { - - } - - public AndroidPlatform (Version apiLevel, string platformID, string? framework = null, bool stable = true, string? apiName = null, string? include = null) - { - if (String.IsNullOrEmpty (platformID)) - throw new ArgumentException ("must not be null or empty", nameof (platformID)); - - ApiName = apiName; - ApiLevel = apiLevel; - PlatformID = platformID; - Framework = framework ?? String.Empty; - Stable = stable; - Supported = !String.IsNullOrEmpty (framework); - Include = include; - } - } - - static class AndroidPlatformExtensions - { - public static void Add (this List list, uint apiLevel, string platformID, string framework, bool stable, string? apiName = null, string? include = null) - { - Add (list, new Version (checked ((int) apiLevel), 0), platformID, framework, stable, apiName, include); - } - - public static void Add (this List list, Version apiLevel, string platformID, string framework, bool stable, string? apiName = null, string? include = null) - { - if (list.Any (p => p.ApiLevel == apiLevel)) - throw new InvalidOperationException ($"Duplicate Android platform, API level {apiLevel}"); - - list.Add (new AndroidPlatform (apiLevel, platformID, framework, stable, apiName, include)); - } - } -} diff --git a/build-tools/xaprepare/xaprepare/Application/GeneratedMonoAndroidProjitemsFile.cs b/build-tools/xaprepare/xaprepare/Application/GeneratedMonoAndroidProjitemsFile.cs deleted file mode 100644 index 31ea06a420c..00000000000 --- a/build-tools/xaprepare/xaprepare/Application/GeneratedMonoAndroidProjitemsFile.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Xamarin.Android.Prepare -{ - class GeneratedMonoAndroidProjitemsFile : GeneratedFile - { - const string FileTop = @" - - - -"; - - const string OutputFileName = "Mono.Android.Apis.projitems"; - - const string FileBottom = @""; - - public GeneratedMonoAndroidProjitemsFile () - : base (Path.Combine (Configurables.Paths.BuildBinDir, OutputFileName)) - {} - - public override void Generate (Context context) - { - using (var fs = File.Open (OutputPath, FileMode.Create)) { - using (var sw = new StreamWriter (fs)) { - GenerateFile (sw); - } - } - } - - void GenerateFile (StreamWriter sw) - { - sw.Write (FileTop); - - sw.WriteLine (" "); - BuildAndroidPlatforms.AllPlatforms.ForEach (androidPlatform => WriteGroupApiInfo (sw, androidPlatform)); - sw.WriteLine (" "); - - sw.Write (FileBottom); - } - - void WriteGroupApiInfo (StreamWriter sw, AndroidPlatform androidPlatform) - { - if (string.IsNullOrWhiteSpace (androidPlatform.ApiName)) { - return; - } - - sw.WriteLine ($" "); - sw.WriteLine ($" {androidPlatform.ApiName}"); - sw.WriteLine ($" {androidPlatform.ApiLevel.Major}"); - sw.WriteLine ($" {androidPlatform.ApiLevel}"); - sw.WriteLine ($" {androidPlatform.PlatformID}"); - sw.WriteLine ($" {androidPlatform.Stable}"); - sw.WriteLine ($" "); - } - } -} \ No newline at end of file diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index d2dd2054524..7ead038f419 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace Xamarin.Android.Prepare { @@ -10,47 +9,5 @@ class BuildAndroidPlatforms public static string NdkMinimumAPI => Context.Instance.Properties.GetRequiredValue (KnownProperties.AndroidMinimumDotNetApiLevel); public static string NdkMinimumAPILegacy32 => NdkMinimumAPI; - - public static readonly List AllPlatforms = new List { - new AndroidPlatform (apiName: "", apiLevel: 1, platformID: "1"), - new AndroidPlatform (apiName: "", apiLevel: 2, platformID: "2"), - new AndroidPlatform (apiName: "", apiLevel: 3, platformID: "3"), - new AndroidPlatform (apiName: "Donut", apiLevel: 4, platformID: "4", include: "v1.6"), - new AndroidPlatform (apiName: "Eclair", apiLevel: 5, platformID: "5", include: "v2.0"), - new AndroidPlatform (apiName: "Eclair", apiLevel: 6, platformID: "6", include: "v2.0.1"), - new AndroidPlatform (apiName: "Eclair", apiLevel: 7, platformID: "7", include: "v2.1"), - new AndroidPlatform (apiName: "Froyo", apiLevel: 8, platformID: "8", include: "v2.2"), - new AndroidPlatform (apiName: "", apiLevel: 9, platformID: "9"), - new AndroidPlatform (apiName: "Gingerbread", apiLevel: 10, platformID: "10", include: "v2.3"), - new AndroidPlatform (apiName: "Honeycomb", apiLevel: 11, platformID: "11", include: "v3.0"), - new AndroidPlatform (apiName: "Honeycomb", apiLevel: 12, platformID: "12", include: "v3.1"), - new AndroidPlatform (apiName: "Honeycomb", apiLevel: 13, platformID: "13", include: "v3.2"), - new AndroidPlatform (apiName: "Ice Cream Sandwich", apiLevel: 14, platformID: "14", include: "v4.0"), - new AndroidPlatform (apiName: "Ice Cream Sandwich", apiLevel: 15, platformID: "15", include: "v4.0.3"), - new AndroidPlatform (apiName: "Jelly Bean", apiLevel: 16, platformID: "16", include: "v4.1"), - new AndroidPlatform (apiName: "Jelly Bean", apiLevel: 17, platformID: "17", include: "v4.2"), - new AndroidPlatform (apiName: "Jelly Bean", apiLevel: 18, platformID: "18", include: "v4.3"), - new AndroidPlatform (apiName: "Kit Kat", apiLevel: 19, platformID: "19", include: "v4.4"), - new AndroidPlatform (apiName: "Kit Kat + Wear support", apiLevel: 20, platformID: "20", include: "v4.4.87"), - new AndroidPlatform (apiName: "Lollipop", apiLevel: 21, platformID: "21", include: "v5.0"), - new AndroidPlatform (apiName: "Lollipop", apiLevel: 22, platformID: "22", include: "v5.1"), - new AndroidPlatform (apiName: "Marshmallow", apiLevel: 23, platformID: "23", include: "v6.0"), - new AndroidPlatform (apiName: "Nougat", apiLevel: 24, platformID: "24", include: "v7.0"), - new AndroidPlatform (apiName: "Nougat", apiLevel: 25, platformID: "25", include: "v7.1"), - new AndroidPlatform (apiName: "Oreo", apiLevel: 26, platformID: "26", include: "v8.0"), - new AndroidPlatform (apiName: "Oreo", apiLevel: 27, platformID: "27", include: "v8.1"), - new AndroidPlatform (apiName: "Pie", apiLevel: 28, platformID: "28", include: "v9.0"), - new AndroidPlatform (apiName: "Q", apiLevel: 29, platformID: "29", include: "v10.0"), - new AndroidPlatform (apiName: "R", apiLevel: 30, platformID: "30", include: "v11.0"), - new AndroidPlatform (apiName: "S", apiLevel: 31, platformID: "31", include: "v12.0"), - new AndroidPlatform (apiName: "Sv2", apiLevel: 32, platformID: "32", include: "v12.1"), - new AndroidPlatform (apiName: "Tiramisu", apiLevel: 33, platformID: "33", include: "v13.0", framework: "v13.0"), - new AndroidPlatform (apiName: "UpsideDownCake", apiLevel: 34, platformID: "34", include: "v14.0", framework: "v14.0"), - new AndroidPlatform (apiName: "VanillaIceCream", apiLevel: 35, platformID: "35", include: "v15.0", framework: "v15.0"), - new AndroidPlatform (apiName: "Baklava", apiLevel: 36, platformID: "36", include: "v16.0", framework: "v16.0"), - new AndroidPlatform (apiName: "CANARY", apiLevel: new Version (36, 1), platformID: "36.1", include: "v16.1", framework: "v16.1", stable: true), - new AndroidPlatform (apiName: "CinnamonBun", apiLevel: new Version (37, 0), platformID: "37.0", include: "v17.0", framework: "v17.0", stable: true), - }; - } } diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs index 255c181ee75..d3671362bc3 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs @@ -76,9 +76,7 @@ protected override async Task Execute (Context context) if (onlyRequired) return null; - var steps = new List { - new GeneratedMonoAndroidProjitemsFile (), - }; + var steps = new List (); AddOSSpecificSteps (context, steps); diff --git a/src/Mono.Android/Mono.Android.Apis.projitems b/src/Mono.Android/Mono.Android.Apis.projitems new file mode 100644 index 00000000000..140c84d053d --- /dev/null +++ b/src/Mono.Android/Mono.Android.Apis.projitems @@ -0,0 +1,244 @@ + + + + + + Donut + 4 + 4.0 + 4 + True + + + Eclair + 5 + 5.0 + 5 + True + + + Eclair + 6 + 6.0 + 6 + True + + + Eclair + 7 + 7.0 + 7 + True + + + Froyo + 8 + 8.0 + 8 + True + + + Gingerbread + 10 + 10.0 + 10 + True + + + Honeycomb + 11 + 11.0 + 11 + True + + + Honeycomb + 12 + 12.0 + 12 + True + + + Honeycomb + 13 + 13.0 + 13 + True + + + Ice Cream Sandwich + 14 + 14.0 + 14 + True + + + Ice Cream Sandwich + 15 + 15.0 + 15 + True + + + Jelly Bean + 16 + 16.0 + 16 + True + + + Jelly Bean + 17 + 17.0 + 17 + True + + + Jelly Bean + 18 + 18.0 + 18 + True + + + Kit Kat + 19 + 19.0 + 19 + True + + + Kit Kat + Wear support + 20 + 20.0 + 20 + True + + + Lollipop + 21 + 21.0 + 21 + True + + + Lollipop + 22 + 22.0 + 22 + True + + + Marshmallow + 23 + 23.0 + 23 + True + + + Nougat + 24 + 24.0 + 24 + True + + + Nougat + 25 + 25.0 + 25 + True + + + Oreo + 26 + 26.0 + 26 + True + + + Oreo + 27 + 27.0 + 27 + True + + + Pie + 28 + 28.0 + 28 + True + + + Q + 29 + 29.0 + 29 + True + + + R + 30 + 30.0 + 30 + True + + + S + 31 + 31.0 + 31 + True + + + Sv2 + 32 + 32.0 + 32 + True + + + Tiramisu + 33 + 33.0 + 33 + True + + + UpsideDownCake + 34 + 34.0 + 34 + True + + + VanillaIceCream + 35 + 35.0 + 35 + True + + + Baklava + 36 + 36.0 + 36 + True + + + CANARY + 36 + 36.1 + 36.1 + True + + + CinnamonBun + 37 + 37.0 + 37.0 + True + + + \ No newline at end of file diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 6f2fab65325..5839eb08327 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -4,7 +4,7 @@ - + @@ -169,7 +169,7 @@ diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 2b15917423a..b2547a30810 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -163,7 +163,7 @@ Date: Tue, 9 Jun 2026 11:07:15 -0500 Subject: [PATCH 3/4] [Xamarin.AndroidTools] Remove dead VS Mac / legacy code (#11607) 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, - } -} From 1043db17c8903003b7ae92e4023a4facd81c05ab Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 9 Jun 2026 14:24:08 -0500 Subject: [PATCH 4/4] [NUnitLite] Add OneTimeSetUp/OneTimeTearDown attribute shims NUnit 3.x renamed [TestFixtureSetUp]/[TestFixtureTearDown] to [OneTimeSetUp]/[OneTimeTearDown]. dotnet/java-interop#1437 migrated the Java.Interop test suite to the new names. The bundled Xamarin.Android.NUnitLite (src-ThirdParty/NUnitLite/) only defines the legacy names. Add no-op subclasses for the new names so test source using [OneTimeSetUp]/[OneTimeTearDown] still compiles against bundled NUnitLite once the Java.Interop submodule is bumped past dotnet/java-interop#1437. These shims become unnecessary once dotnet/android moves off bundled NUnitLite to stock NUnit (tracked in PR #11224). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Attributes/OneTimeSetUpAttribute.cs | 37 +++++++++++++++++++ .../Attributes/OneTimeTearDownAttribute.cs | 37 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src-ThirdParty/NUnitLite/Attributes/OneTimeSetUpAttribute.cs create mode 100644 src-ThirdParty/NUnitLite/Attributes/OneTimeTearDownAttribute.cs diff --git a/src-ThirdParty/NUnitLite/Attributes/OneTimeSetUpAttribute.cs b/src-ThirdParty/NUnitLite/Attributes/OneTimeSetUpAttribute.cs new file mode 100644 index 00000000000..4f7104e9fc5 --- /dev/null +++ b/src-ThirdParty/NUnitLite/Attributes/OneTimeSetUpAttribute.cs @@ -0,0 +1,37 @@ +// *********************************************************************** +// Copyright (c) 2009 Charlie Poole +// +// 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. +// *********************************************************************** + +// Compat shim so test source using NUnit 3.x [OneTimeSetUp]/[OneTimeTearDown] still compiles against Xamarin.Android.NUnitLite. +namespace NUnit.Framework +{ + using System; + + /// + /// Compatibility alias for so test source + /// targeting NUnit 3.x's [OneTimeSetUp] name compiles against bundled NUnitLite. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)] + public class OneTimeSetUpAttribute : TestFixtureSetUpAttribute + { + } +} diff --git a/src-ThirdParty/NUnitLite/Attributes/OneTimeTearDownAttribute.cs b/src-ThirdParty/NUnitLite/Attributes/OneTimeTearDownAttribute.cs new file mode 100644 index 00000000000..290ab470552 --- /dev/null +++ b/src-ThirdParty/NUnitLite/Attributes/OneTimeTearDownAttribute.cs @@ -0,0 +1,37 @@ +// *********************************************************************** +// Copyright (c) 2009 Charlie Poole +// +// 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. +// *********************************************************************** + +// Compat shim so test source using NUnit 3.x [OneTimeSetUp]/[OneTimeTearDown] still compiles against Xamarin.Android.NUnitLite. +namespace NUnit.Framework +{ + using System; + + /// + /// Compatibility alias for so test source + /// targeting NUnit 3.x's [OneTimeTearDown] name compiles against bundled NUnitLite. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)] + public class OneTimeTearDownAttribute : TestFixtureTearDownAttribute + { + } +}