diff --git a/Documentation/guides/BuildProcess.md b/Documentation/guides/BuildProcess.md index 2fcaefaedd7..ed54fac3553 100644 --- a/Documentation/guides/BuildProcess.md +++ b/Documentation/guides/BuildProcess.md @@ -222,7 +222,8 @@ when packaging Release applications. - **AndroidApkDigestAlgorithm** – A string value which specifies the digest algorithm to use with `jarsigner -digestalg`. - The default value is `SHA1` for APKs and `SHA-256` for App Bundles. + The default value is `SHA-256`, which was `SHA1` in previous + versions of Xamarin.Android. Added in Xamarin.Android 9.4. @@ -234,7 +235,8 @@ when packaging Release applications. - **AndroidApkSigningAlgorithm** – A string value which specifies the signing algorithm to use with `jarsigner -sigalg`. - The default value is `md5withRSA` for APKs and `SHA256withRSA` for App Bundles. + The default value is `SHA256withRSA`, which was `md5withRSA` in + previous versions of Xamarin.Android. Added in Xamarin.Android 8.2. @@ -599,6 +601,14 @@ when packaging Release applications. [apk]: https://en.wikipedia.org/wiki/Android_application_package [bundle]: https://developer.android.com/platform/technology/app-bundle +- **AndroidPackageNamingPolicy** – An enum-style property for + specifying the Java package names of generated Java source code. + The default value is `LowercaseCrc64`. In previous versions of + Xamarin.Android, MD5-based names were used. You can restore the old + behavior by using `LowercaseMD5`. + + Added in Xamarin.Android 10.1. + - **AndroidR8JarPath** – The path to `r8.jar` for use with the r8 dex-compiler and shrinker. Defaults to a path in the Xamarin.Android installation. For further information see our diff --git a/Documentation/guides/messages/xa0120.md b/Documentation/guides/messages/xa0120.md new file mode 100644 index 00000000000..b4584da8700 --- /dev/null +++ b/Documentation/guides/messages/xa0120.md @@ -0,0 +1,18 @@ +--- +title: Xamarin.Android warning XA0120 +description: XA0120 warning code +ms.date: 09/19/2019 +--- +# Xamarin.Android warning XA0120 + +## Issue + +This warning indicates that SHA1 algorithm usage failed. This can be +caused by a [FIPS][fips] group policy setting on Windows 10 machines. + +## Solution + +Disable [FIPS][fips] on your system or use newer versions of the NuGet +package that encounters this failure. + +[fips]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/system-cryptography-use-fips-compliant-algorithms-for-encryption-hashing-and-signing diff --git a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs index 764fea6d83f..b9fa35b9076 100644 --- a/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs +++ b/build-tools/xaprepare/xaprepare/ThirdPartyNotices/Java.Interop.cs @@ -11,6 +11,7 @@ class JavaInterop_External_Dependencies_Group : ThirdPartyNoticeGroup public override List Notices => new List { new JavaInterop_xamarin_Java_Interop_TPN (), + new JavaInterop_gityf_crc_TPN (), new JavaInterop_xamarin_mono_cecil_TPN (), new JavaInterop_jonpryor_mono_linq_expressions_TPN (), new JavaInterop_mono_csharp_TPN (), @@ -31,6 +32,44 @@ class JavaInterop_xamarin_Java_Interop_TPN : ThirdPartyNotice public override string LicenseText => null; } + class JavaInterop_gityf_crc_TPN : ThirdPartyNotice + { + static readonly Uri url = new Uri ("https://github.com/gityf/crc"); + + public override string LicenseFile => null; + public override string Name => "gityf/crc"; + public override Uri SourceUrl => url; + + public override string LicenseText => @" +Copyright (c) 2012, Salvatore Sanfilippo +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Redis nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +"; + } + // git submodules of Java.Interop class JavaInterop_xamarin_mono_cecil_TPN : ThirdPartyNotice { diff --git a/external/Java.Interop b/external/Java.Interop index 4fd35393fd2..b98f42323a8 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 4fd35393fd261603e54a87928cfda26e02225d45 +Subproject commit b98f42323a8e14c46cb4f96b2472f5151824b1b9 diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index b17086da4c9..6555ed732fe 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -239,6 +239,11 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) Java.Lang.Thread.DefaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler; } + var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__"); + if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) { + JavaNativeTypeManager.PackageNamingPolicy = pnp; + } + if (Logger.LogTiming) { totalElapsed += stopper.ElapsedMilliseconds; Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize end: elapsed {totalElapsed} ms"); diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index f823243a623..2fc26351cff 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -86,6 +86,9 @@ JavaNativeTypeManager.cs + + Crc64.cs + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs index e4353e90589..ac236d9ef96 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs @@ -53,13 +53,13 @@ public class AndroidSignPackage : AndroidRunToolTask public string TimestampAuthorityCertificateAlias { get; set; } /// - /// -sigalg switch, should be md5withRSA for APKs or SHA256withRSA for App Bundles + /// -sigalg switch, which is SHA256withRSA by default. Previous versions of XA was md5withRSA. /// [Required] public string SigningAlgorithm { get; set; } /// - /// -digestalg switch, should be SHA1 for APKs or SHA-256 for App Bundles + /// -digestalg switch, which is SHA-256 by default. Previous versions of XA was SHA1. /// [Required] public string DigestAlgorithm { get; set; } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CalculateAdditionalResourceCacheDirectories.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CalculateAdditionalResourceCacheDirectories.cs index 0ff7ce52dca..bef66d9a9a9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CalculateAdditionalResourceCacheDirectories.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CalculateAdditionalResourceCacheDirectories.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.IO; -using System.Security.Cryptography; -using System.Text.RegularExpressions; -using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks { @@ -28,15 +26,11 @@ public override bool RunTask () if (!AdditionalAndroidResourcePaths.Any ()) return true; - var md5 = MD5.Create (); Directory.CreateDirectory (CacheDirectory); var directories = new List (); foreach (var path in AdditionalAndroidResourcePaths) { - var cacheSubDirectory = string.Concat (md5.ComputeHash ( - Encoding.UTF8.GetBytes (path)).Select (b => b.ToString ("X02")) - ); - var targetDir = Path.Combine (CacheDirectory, cacheSubDirectory); + var targetDir = Path.Combine (CacheDirectory, Files.HashString (path)); directories.Add (new TaskItem (Path.GetFullPath (targetDir).TrimEnd (Path.DirectorySeparatorChar))); } AdditionalResourceCachePaths = directories.ToArray (); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ComputeHash.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ComputeHash.cs index 8bd3c78228e..29d909ba62f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ComputeHash.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ComputeHash.cs @@ -1,51 +1,36 @@ -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; using Microsoft.Build.Utilities; using Microsoft.Build.Framework; +using Xamarin.Android.Tools; -namespace Xamarin.Android.Tasks { - public class ComputeHash : AndroidTask { +namespace Xamarin.Android.Tasks +{ + public class ComputeHash : AndroidTask + { public override string TaskPrefix => "CPT"; - List output = new List (); - [Required] public ITaskItem [] Source { get; set; } public bool CopyMetaData { get; set; } = true; [Output] - public ITaskItem [] Output { get => output.ToArray (); } + public ITaskItem [] Output { get; set; } public override bool RunTask () { - using (var sha1 = SHA1.Create ()) { - - foreach (var item in Source) { - var newItem = new TaskItem(item.ItemSpec, new Dictionary() { - { "Hash", HashItemSpec (sha1, item.ItemSpec) } - }); - if (CopyMetaData) - item.CopyMetadataTo (newItem); - output.Add (newItem); - } - Log.LogDebugTaskItems ("Output : ", Output); - return !Log.HasLoggedErrors; - } - } - - string HashItemSpec (SHA1 sha1, string hashInput) - { - var hash = sha1.ComputeHash (Encoding.UTF8.GetBytes (hashInput)); - var hashResult = new StringBuilder (hash.Length * 2); - - foreach (byte b in hash) { - hashResult.Append (b.ToString ("x2")); + var output = new List (Source.Length); + foreach (var item in Source) { + var newItem = new TaskItem(item.ItemSpec, new Dictionary() { + { "Hash", Files.HashString (item.ItemSpec) } + }); + if (CopyMetaData) + item.CopyMetadataTo (newItem); + output.Add (newItem); } - return hashResult.ToString (); + Output = output.ToArray (); + Log.LogDebugTaskItems ("Output : ", Output); + return !Log.HasLoggedErrors; } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Desugar.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Desugar.cs index 359448208e6..9cdb6bf7e32 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Desugar.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Desugar.cs @@ -1,11 +1,8 @@ // Copyright (C) 2011 Xamarin, Inc. All rights reserved. -using System; -using System.Linq; using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using System.Text; using System.Collections.Generic; using Xamarin.Android.Tools; @@ -87,9 +84,8 @@ protected override string GenerateCommandLineCommands () cmd.AppendSwitch (DesugarExtraArguments); // it should contain "--dex". var outputs = new List (); - var md5 = System.Security.Cryptography.MD5.Create (); foreach (var jar in InputJars) { - var output = Path.Combine (OutputDirectory, BitConverter.ToString (md5.ComputeHash (Encoding.UTF8.GetBytes (jar))) + Path.GetFileName (jar)); + var output = Path.Combine (OutputDirectory, Files.HashString (jar) + Path.GetFileName (jar)); outputs.Add (output); cmd.AppendSwitch ("--classpath_entry "); cmd.AppendFileNameIfNotNull (jar); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAdditionalResourcesFromAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAdditionalResourcesFromAssemblies.cs index 2424700dc5c..8cbbaf6dde7 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAdditionalResourcesFromAssemblies.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAdditionalResourcesFromAssemblies.cs @@ -7,11 +7,9 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Security.Cryptography; -using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using Xamarin.Android.Tools; -using Xamarin.Build; namespace Xamarin.Android.Tasks { @@ -52,7 +50,6 @@ public class GetAdditionalResourcesFromAssemblies : AndroidAsyncTask public string CacheFile { get; set; } string CachePath; - MD5 md5 = MD5.Create (); internal const string AndroidSkipResourceExtraction = "AndroidSkipResourceExtraction"; @@ -206,12 +203,22 @@ bool IsValidDownload(string file, string sha1) if (string.IsNullOrEmpty (sha1)) return true; + HashAlgorithm hashAlgorithm; + try { + hashAlgorithm = new SHA1Managed (); + } catch (InvalidOperationException exc) { + // FIPS-enabled Windows machines will fail here + Log.LogCodedWarning ("XA0120", $"Failed to use SHA1 hash algorithm: {exc}"); + return true; + } + var hashFile = file + ".sha1"; + using (hashAlgorithm) if (File.Exists (hashFile) && string.Compare (File.ReadAllText (hashFile), sha1, StringComparison.InvariantCultureIgnoreCase) == 0) return true; - - var hash = Xamarin.Android.Tools.Files.HashFile (file).Replace ("-", String.Empty); - LogDebugMessage ("File :{0}", file); + + var hash = Files.HashFile (file, hashAlgorithm); + LogDebugMessage ("File : {0}", file); LogDebugMessage ("SHA1 : {0}", hash); LogDebugMessage ("Expected SHA1 : {0}", sha1); @@ -294,7 +301,7 @@ string MakeSureLibraryIsInPlace (string destinationBase, string url, string vers if (createDestinationDirectory) Directory.CreateDirectory (destinationDir); - var hash = string.Concat (md5.ComputeHash (Encoding.UTF8.GetBytes (url)).Select (b => b.ToString ("X02"))); + var hash = Files.HashString (url); var uri = new Uri (url); var extraPath = extraPaths.FirstOrDefault (x => File.Exists (Path.Combine (AndroidSdkDirectory, x, embeddedArchive ?? String.Empty))); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetConvertedJavaLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetConvertedJavaLibraries.cs index 123a03efdae..cff79a0bd2a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetConvertedJavaLibraries.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetConvertedJavaLibraries.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Security.Cryptography; -using System.Text; using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; +using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks { @@ -22,11 +18,10 @@ public class GetConvertedJavaLibraries : AndroidTask public override bool RunTask () { - var md5 = MD5.Create (); ConvertedFilesToBeGenerated = (JarsToConvert ?? new string [0]).Select ( j => Path.Combine (OutputJackDirectory, - BitConverter.ToString (md5.ComputeHash (Encoding.UTF8.GetBytes (j))) + Path.ChangeExtension (Path.GetFileName (j), Extension))) + Files.HashString (j) + Path.ChangeExtension (Path.GetFileName (j), Extension))) .ToArray (); Log.LogDebugTaskItems (" ConvertedFilesToBeGenerated:", ConvertedFilesToBeGenerated); return true; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs index 4f1d825ad96..76d959555eb 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs @@ -1,15 +1,13 @@ using System; -using Xamarin.ProjectTools; -using NUnit.Framework; -using System.Linq; +using System.Collections.Generic; using System.IO; -using System.Threading; +using System.Linq; using System.Text; -using System.Collections.Generic; -using Microsoft.Build.Framework; -using System.Xml.Linq; -using System.Security.Cryptography; using System.Text.RegularExpressions; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using NUnit.Framework; +using Xamarin.Android.Tools; using Xamarin.ProjectTools; namespace Xamarin.Android.Build.Tests @@ -49,8 +47,7 @@ public void DesignTimeBuild ([Values(false, true)] bool isRelease, [Values (fals { "XAMARIN_CACHEPATH", cachePath }, }; var url = "http://dl-ssl.google.com/android/repository/build-tools_r24-macosx.zip"; - var md5 = MD5.Create (); - var hash = string.Concat (md5.ComputeHash (Encoding.UTF8.GetBytes (url)).Select (b => b.ToString ("X02"))); + var hash = Files.HashString (url); var zipPath = Path.Combine (cachePath, "zips", $"{hash}.zip"); if (File.Exists (zipPath)) File.Delete (zipPath); @@ -445,7 +442,7 @@ protected override void OnClick() var doc = XDocument.Load (preferencesPath); Assert.IsNotNull (doc.Element ("PreferenceScreen"), "PreferenceScreen should be present in preferences.xml"); Assert.IsNull (doc.Element ("PreferenceScreen").Element ("UnnamedProject.CustomPreference"), - "UnamedProject.CustomPreference should have been replaced with an $(MD5Hash).CustomPreference"); + "UnamedProject.CustomPreference should have been replaced with an $(Hash).CustomPreference"); var style = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "res", "values", "styles.xml"); Assert.IsTrue (File.Exists (style)); doc = XDocument.Load (style); @@ -487,9 +484,9 @@ void CheckCustomView (Xamarin.Tools.Zip.ZipArchive zip, params string [] paths) var doc = XDocument.Load (customViewPath); Assert.IsNotNull (doc.Element ("LinearLayout"), "PreferenceScreen should be present in preferences.xml"); Assert.IsNull (doc.Element ("LinearLayout").Element ("Classlibrary1.CustomTextView"), - "Classlibrary1.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "Classlibrary1.CustomTextView should have been replaced with an $(Hash).CustomTextView"); Assert.IsNull (doc.Element ("LinearLayout").Element ("classlibrary1.CustomTextView"), - "classlibrary1.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "classlibrary1.CustomTextView should have been replaced with an $(Hash).CustomTextView"); //Now check the zip var customViewInZip = "res/layout/" + Path.GetFileName (customViewPath); @@ -505,9 +502,9 @@ void CheckCustomView (Xamarin.Tools.Zip.ZipArchive zip, params string [] paths) // Don't use `StringAssert` because `contents` make the failure message unreadable. var contents = reader.ReadToEnd (); Assert.IsFalse (contents.Contains ("Classlibrary1.CustomTextView"), - "Classlibrary1.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "Classlibrary1.CustomTextView should have been replaced with an $(Hash).CustomTextView"); Assert.IsFalse (contents.Contains ("classlibrary1.CustomTextView"), - "classlibrary1.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "classlibrary1.CustomTextView should have been replaced with an $(Hash).CustomTextView"); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 65962293f82..703aff4762b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -865,7 +865,7 @@ public void BuildProguardEnabledProject ([Values (true, false)] bool isRelease, if (isRelease && !string.IsNullOrEmpty (linkTool)) { var proguardProjectPrimary = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "proguard", "proguard_project_primary.cfg"); FileAssert.Exists (proguardProjectPrimary); - Assert.IsTrue (StringAssertEx.ContainsText (File.ReadAllLines (proguardProjectPrimary), "-keep class md52d9cf6333b8e95e8683a477bc589eda5.MainActivity"), "`md52d9cf6333b8e95e8683a477bc589eda5.MainActivity` should exist in `proguard_project_primary.cfg`!"); + Assert.IsTrue (StringAssertEx.ContainsText (File.ReadAllLines (proguardProjectPrimary), $"-keep class {proj.JavaPackageName}.MainActivity"), $"`{proj.JavaPackageName}.MainActivity` should exist in `proguard_project_primary.cfg`!"); } var dexFile = b.Output.GetIntermediaryPath (Path.Combine ("android", "bin", "classes.dex")); @@ -3877,9 +3877,9 @@ public void AssemblyWithMissingTargetFramework () Assert.IsTrue (b.Build (proj), "build should have succeeded."); // We should have a java stub - var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); - var javaStub = Path.Combine (intermediate, "android", "src", "md54908d67eb9afef4acc92753cc61471e9", "CircleImageView.java"); - FileAssert.Exists (javaStub); + var javaStubDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "src"); + var files = Directory.GetFiles (javaStubDir, "CircleImageView.java", SearchOption.AllDirectories); + CollectionAssert.IsNotEmpty (files, $"{javaStubDir} should contain CircleImageView.java!"); } } @@ -4046,5 +4046,19 @@ public void AbiNameInIntermediateOutputPath () Assert.IsFalse (StringAssertEx.ContainsText (b.LastBuildOutput, Path.Combine ("armeabi", "libe_sqlite3.so")), "Build should not use `armeabi`."); } } + + [Test] + public void PackageNamingPolicy ([Values ("LowercaseMD5", "LowercaseCrc64")] string packageNamingPolicy) + { + var proj = new XamarinAndroidApplicationProject (); + proj.SetProperty ("AndroidPackageNamingPolicy", packageNamingPolicy); + proj.SetProperty (KnownProperties.AndroidSupportedAbis, "armeabi-v7a;x86"); + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "build should have succeeded."); + var environment = b.Output.GetIntermediaryPath (Path.Combine ("__environment__.txt")); + FileAssert.Exists (environment); + Assert.AreEqual ($"__XA_PACKAGE_NAMING_POLICY__={packageNamingPolicy}", File.ReadAllText (environment).Trim ()); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/DesignerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/DesignerTests.cs index 35c03da3a87..1609d3cbe57 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/DesignerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/DesignerTests.cs @@ -105,9 +105,9 @@ public CustomTextView(Context context, IAttributeSet attributes) : base(context, Assert.IsTrue (File.Exists (customViewPath), $"custom_text.xml should exist at {customViewPath}"); var doc = XDocument.Load (customViewPath); Assert.IsNotNull (doc.Element ("LinearLayout").Element ("UnnamedProject.CustomTextView"), - "UnnamedProject.CustomTextView should have not been replaced with an $(MD5Hash).CustomTextView"); + "UnnamedProject.CustomTextView should have not been replaced with a $(Hash).CustomTextView"); Assert.IsNotNull (doc.Element ("LinearLayout").Element ("unnamedproject.CustomTextView"), - "unnamedproject.CustomTextView should have not been replaced with an $(MD5Hash).CustomTextView"); + "unnamedproject.CustomTextView should have not been replaced with a $(Hash).CustomTextView"); // Build the library project now Assert.IsTrue (libb.Build (lib), "library build should have succeeded."); appb.Target = "Build"; @@ -116,18 +116,18 @@ public CustomTextView(Context context, IAttributeSet attributes) : base(context, Assert.IsTrue (appb.Output.AreTargetsAllBuilt ("_Foo"), "_Foo should have run completely"); doc = XDocument.Load (customViewPath); Assert.IsNull (doc.Element ("LinearLayout").Element ("UnnamedProject.CustomTextView"), - "UnnamedProject.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "UnnamedProject.CustomTextView should have been replaced with a $(Hash).CustomTextView"); Assert.IsNull (doc.Element ("LinearLayout").Element ("unnamedproject.CustomTextView"), - "unnamedproject.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "unnamedproject.CustomTextView should have been replaced with a $(Hash).CustomTextView"); appb.Target = target; Assert.IsTrue (appb.Build (proj, parameters: DesignerParameters), $"build should have succeeded for target `{target}`"); Assert.IsTrue (appb.Output.AreTargetsAllSkipped ("_UpdateAndroidResgen"), "_UpdateAndroidResgen should have been skipped."); Assert.IsTrue (appb.Output.AreTargetsAllBuilt ("_Foo"), "_Foo should have run completely"); doc = XDocument.Load (customViewPath); Assert.IsNull (doc.Element ("LinearLayout").Element ("UnnamedProject.CustomTextView"), - "UnnamedProject.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "UnnamedProject.CustomTextView should have been replaced with a $(Hash).CustomTextView"); Assert.IsNull (doc.Element ("LinearLayout").Element ("unnamedproject.CustomTextView"), - "unnamedproject.CustomTextView should have been replaced with an $(MD5Hash).CustomTextView"); + "unnamedproject.CustomTextView should have been replaced with a $(Hash).CustomTextView"); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 04313c65749..cc218c4fb58 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -950,5 +950,27 @@ public void DeterministicBuilds ([Values (true, false)] bool deterministic) } } } + + [Test] + public void ChangePackageNamingPolicy () + { + var proj = new XamarinAndroidApplicationProject (); + proj.Sources.Add (new BuildItem.Source ("Bar.cs") { + TextContent = () => "namespace Foo { class Bar : Java.Lang.Object { } }" + }); + proj.SetProperty ("AndroidPackageNamingPolicy", "LowercaseMD5"); + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "first build should have succeeded."); + var dexFile = b.Output.GetIntermediaryPath (Path.Combine ("android", "bin", "classes.dex")); + var className = "Lmd5aaee5c01293e648941eac447719ef3fb/Bar;"; + Assert.IsTrue (DexUtils.ContainsClass (className, dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!"); + + proj.SetProperty ("AndroidPackageNamingPolicy", "LowercaseCrc64"); + Assert.IsTrue (b.Build (proj), "second build should have succeeded."); + Assert.IsFalse (DexUtils.ContainsClass (className, dexFile, AndroidSdkPath), $"`{dexFile}` should *not* include `{className}`!"); + className = "Lcrc64dca3aed1e0ff8a1a/Bar;"; + Assert.IsTrue (DexUtils.ContainsClass (className, dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!"); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs index 44fb3552422..a652197d25e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs @@ -247,8 +247,8 @@ public void DirectBootAwareAttribute () TargetFrameworkVersion = "v7.0", IsRelease = true, }; - string attrHead = "[Activity ("; - string attr = @"[Activity (DirectBootAware=true, "; + string attrHead = ", Activity ("; + string attr = @", Activity (DirectBootAware=true, "; proj.MainActivity = proj.DefaultMainActivity.Replace (attrHead, attr); proj.OtherBuildItems.Add (new BuildItem (BuildActions.Compile, "MyService.cs") { TextContent = () => "using Android.App; [Service (DirectBootAware = true)] public class MyService : Service { public override Android.OS.IBinder OnBind (Android.Content.Intent intent) { return null; } }" @@ -658,7 +658,7 @@ public class TestActivity2 : FragmentActivity { [Test] public void AllActivityAttributeProperties () { - const string expectedOutput = "android:allowEmbedded=\"true\" android:allowTaskReparenting=\"true\" android:alwaysRetainTaskState=\"true\" android:autoRemoveFromRecents=\"true\" android:banner=\"@drawable/icon\" android:clearTaskOnLaunch=\"true\" android:colorMode=\"hdr\" android:configChanges=\"mcc\" android:description=\"@string/app_name\" android:directBootAware=\"true\" android:documentLaunchMode=\"never\" android:enabled=\"true\" android:enableVrMode=\"foo\" android:excludeFromRecents=\"true\" android:exported=\"true\" android:finishOnCloseSystemDialogs=\"true\" android:finishOnTaskLaunch=\"true\" android:hardwareAccelerated=\"true\" android:icon=\"@drawable/icon\" android:immersive=\"true\" android:label=\"TestActivity\" android:launchMode=\"singleTop\" android:lockTaskMode=\"normal\" android:logo=\"@drawable/icon\" android:maxAspectRatio=\"1.2\" android:maxRecents=\"1\" android:multiprocess=\"true\" android:name=\"com.contoso.TestActivity\" android:noHistory=\"true\" android:parentActivityName=\"md52d9cf6333b8e95e8683a477bc589eda5.MainActivity\" android:permission=\"com.contoso.permission.TEST_ACTIVITY\" android:persistableMode=\"persistNever\" android:process=\"com.contoso.process.testactivity_process\" android:recreateOnConfigChanges=\"mcc\" android:relinquishTaskIdentity=\"true\" android:resizeableActivity=\"true\" android:resumeWhilePausing=\"true\" android:rotationAnimation=\"crossfade\" android:roundIcon=\"@drawable/icon\" android:screenOrientation=\"portrait\" android:showForAllUsers=\"true\" android:showOnLockScreen=\"true\" android:showWhenLocked=\"true\" android:singleUser=\"true\" android:stateNotNeeded=\"true\" android:supportsPictureInPicture=\"true\" android:taskAffinity=\"com.contoso\" android:theme=\"@android:style/Theme.Light\" android:turnScreenOn=\"true\" android:uiOptions=\"splitActionBarWhenNarrow\" android:visibleToInstantApps=\"true\" android:windowSoftInputMode=\"stateUnchanged|adjustUnspecified\""; + const string expectedOutput = "android:allowEmbedded=\"true\" android:allowTaskReparenting=\"true\" android:alwaysRetainTaskState=\"true\" android:autoRemoveFromRecents=\"true\" android:banner=\"@drawable/icon\" android:clearTaskOnLaunch=\"true\" android:colorMode=\"hdr\" android:configChanges=\"mcc\" android:description=\"@string/app_name\" android:directBootAware=\"true\" android:documentLaunchMode=\"never\" android:enabled=\"true\" android:enableVrMode=\"foo\" android:excludeFromRecents=\"true\" android:exported=\"true\" android:finishOnCloseSystemDialogs=\"true\" android:finishOnTaskLaunch=\"true\" android:hardwareAccelerated=\"true\" android:icon=\"@drawable/icon\" android:immersive=\"true\" android:label=\"TestActivity\" android:launchMode=\"singleTop\" android:lockTaskMode=\"normal\" android:logo=\"@drawable/icon\" android:maxAspectRatio=\"1.2\" android:maxRecents=\"1\" android:multiprocess=\"true\" android:name=\"com.contoso.TestActivity\" android:noHistory=\"true\" android:parentActivityName=\"unnamedproject.unnamedproject.MainActivity\" android:permission=\"com.contoso.permission.TEST_ACTIVITY\" android:persistableMode=\"persistNever\" android:process=\"com.contoso.process.testactivity_process\" android:recreateOnConfigChanges=\"mcc\" android:relinquishTaskIdentity=\"true\" android:resizeableActivity=\"true\" android:resumeWhilePausing=\"true\" android:rotationAnimation=\"crossfade\" android:roundIcon=\"@drawable/icon\" android:screenOrientation=\"portrait\" android:showForAllUsers=\"true\" android:showOnLockScreen=\"true\" android:showWhenLocked=\"true\" android:singleUser=\"true\" android:stateNotNeeded=\"true\" android:supportsPictureInPicture=\"true\" android:taskAffinity=\"com.contoso\" android:theme=\"@android:style/Theme.Light\" android:turnScreenOn=\"true\" android:uiOptions=\"splitActionBarWhenNarrow\" android:visibleToInstantApps=\"true\" android:windowSoftInputMode=\"stateUnchanged|adjustUnspecified\""; var proj = new XamarinAndroidApplicationProject (); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MaxPathTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MaxPathTests.cs index 6a6c4596ac2..a4e1991c498 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MaxPathTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MaxPathTests.cs @@ -36,15 +36,24 @@ public void Setup () /// int BaseLength => Path.Combine (Root, "temp").Length + 1; + XamarinProject CreateProject (bool xamarinForms) + { + var proj = xamarinForms ? + new XamarinFormsAndroidApplicationProject () : + new XamarinAndroidApplicationProject (); + // Force the old MD5 naming policy, and remove [Register] + proj.SetProperty ("AndroidPackageNamingPolicy", "LowercaseHash"); + proj.MainActivity = proj.DefaultMainActivity.Replace ("Register (\"${JAVA_PACKAGENAME}.MainActivity\"), ", ""); + return proj; + } + [Test] [TestCaseSource (nameof (EdgeCases))] public void Edge (int limit, bool xamarinForms) { var testName = $"{nameof (Edge)}{xamarinForms}" .PadRight (Files.MaxPath - BaseLength - limit, 'N'); - var proj = xamarinForms ? - new XamarinFormsAndroidApplicationProject () : - new XamarinAndroidApplicationProject (); + var proj = CreateProject (xamarinForms); using (var b = CreateApkBuilder (Path.Combine ("temp", testName))) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); Assert.IsTrue (b.Clean (proj), "Clean should have succeeded."); @@ -69,9 +78,7 @@ public void OverTheEdge (int limit, bool xamarinForms) { var testName = $"{nameof (Edge)}{xamarinForms}" .PadRight (Files.MaxPath - BaseLength - limit, 'N'); - var proj = xamarinForms ? - new XamarinFormsAndroidApplicationProject () : - new XamarinAndroidApplicationProject (); + var proj = CreateProject (xamarinForms); using (var b = CreateApkBuilder (Path.Combine ("temp", testName))) { b.ThrowOnBuildFailure = false; Assert.IsFalse (b.Build (proj), "Build should have failed."); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index 6f728f5359e..04575b33acc 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -237,12 +237,15 @@ void AssertEmbeddedDSOs (string apk) public void ExplicitPackageNamingPolicy () { var proj = new XamarinAndroidApplicationProject (); + proj.Sources.Add (new BuildItem.Source ("Bar.cs") { + TextContent = () => "namespace Foo { class Bar : Java.Lang.Object { } }" + }); proj.SetProperty (proj.DebugProperties, "AndroidPackageNamingPolicy", "Lowercase"); using (var b = CreateApkBuilder (Path.Combine ("temp", TestContext.CurrentContext.Test.Name))) { b.Verbosity = Microsoft.Build.Framework.LoggerVerbosity.Diagnostic; Assert.IsTrue (b.Build (proj), "build failed"); - var text = b.Output.GetIntermediaryAsText (b.Output.IntermediateOutputPath, Path.Combine ("android", "src", "unnamedproject", "MainActivity.java")); - Assert.IsTrue (text.Contains ("package unnamedproject;"), "expected package not found in the source."); + var text = b.Output.GetIntermediaryAsText (b.Output.IntermediateOutputPath, Path.Combine ("android", "src", "foo", "Bar.java")); + Assert.IsTrue (text.Contains ("package foo;"), "expected package not found in the source."); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/Aapt2Tests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/Aapt2Tests.cs index 302f44c2e89..de72110f143 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/Aapt2Tests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/Aapt2Tests.cs @@ -1,33 +1,17 @@ -using System; using System.Collections.Generic; -using NUnit.Framework; -using Xamarin.ProjectTools; using System.IO; using System.Linq; using Microsoft.Build.Framework; -using System.Text; -using Xamarin.Android.Tasks; using Microsoft.Build.Utilities; -using System.Security.Cryptography; - -namespace Xamarin.Android.Build.Tests { - public class Aapt2Tests : BaseTest { - - string GetHash (string hashInput) - { - using (var sha1 = SHA1.Create ()) - { - var hash = sha1.ComputeHash (Encoding.UTF8.GetBytes (hashInput)); - var hashResult = new StringBuilder (hash.Length * 2); - - foreach (byte b in hash) - { - hashResult.Append (b.ToString ("x2")); - } - return hashResult.ToString (); - } - } +using NUnit.Framework; +using Xamarin.Android.Tasks; +using Xamarin.Android.Tools; +using Xamarin.ProjectTools; +namespace Xamarin.Android.Build.Tests +{ + public class Aapt2Tests : BaseTest + { void CallAapt2Compile (IBuildEngine engine, string dir, string outputPath, List output = null) { var errors = new List (); @@ -36,7 +20,7 @@ void CallAapt2Compile (IBuildEngine engine, string dir, string outputPath, List< ToolPath = GetPathToAapt2 (), ResourceDirectories = new ITaskItem [] { new TaskItem(dir, new Dictionary { - { "Hash", output != null ? GetHash (dir) : "compiled" } + { "Hash", output != null ? Files.HashString (dir) : "compiled" } }), }, FlatArchivesDirectory = outputPath, diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs index 3fe7881bc5a..98f3554a08c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs @@ -1,19 +1,13 @@ using NUnit.Framework; using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Text; -using System.Text.RegularExpressions; using System.Threading; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; +using Xamarin.Android.Tasks; using Xamarin.ProjectTools; namespace Xamarin.Android.Build.Tests @@ -206,6 +200,7 @@ protected static string RunAdbCommand (string command, bool ignoreErrors = true, protected static string RunProcess (string exe, string args) { + TestContext.Out.WriteLine ($"{nameof(RunProcess)}: {exe} {args}"); var info = new ProcessStartInfo (exe, args) { RedirectStandardOutput = true, RedirectStandardError = true, @@ -379,29 +374,7 @@ protected ProjectBuilder CreateDllBuilder (string directory = null, bool cleanup return BuildHelper.CreateDllBuilder (directory, cleanupAfterSuccessfulBuild, cleanupOnDispose); } - protected bool FileCompare (string file1, string file2) - { - if (!File.Exists (file1) || !File.Exists (file2)) - return false; - using (var stream1 = File.OpenRead (file1)) { - using (var stream2 = File.OpenRead (file2)) { - return StreamCompare (stream1, stream2); - } - } - } - - protected bool StreamCompare (Stream stream1, Stream stream2) - { - Assert.IsNotNull (stream1, "stream1 of StreamCompare should not be null"); - Assert.IsNotNull (stream2, "stream2 of StreamCompare should not be null"); - byte[] f1 = ReadAllBytesIgnoringLineEndings (stream1); - byte[] f2 = ReadAllBytesIgnoringLineEndings (stream2); - - var hash = MD5.Create (); - var f1hash = Convert.ToBase64String (hash.ComputeHash (f1)); - var f2hash = Convert.ToBase64String (hash.ComputeHash (f2)); - return f1hash.Equals (f2hash); - } + protected bool FileCompare (string file1, string file2) => !MonoAndroidHelper.HasFileChanged (file1, file2); protected byte[] ReadAllBytesIgnoringLineEndings (Stream stream) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/FilesTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/FilesTests.cs index 2b0b94843f6..737149f0315 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/FilesTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/FilesTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using System; using System.IO; using System.Text; using Xamarin.Android.Tools; @@ -385,5 +386,13 @@ public void ExtractAll_MacOSFiles () Assert.IsFalse (changes, "ExtractAll should *not* report changes."); DirectoryAssert.DoesNotExist (tempDir); } + + [Test] + public void ToHashString () + { + var bytes = new byte [] { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; + var expected = BitConverter.ToString (bytes).Replace ("-", string.Empty); + Assert.AreEqual (expected, Files.ToHexString (bytes)); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs index 6688e1cc46d..59d8cbd2186 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs @@ -50,6 +50,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug" LayoutMain = default_layout_main; StringsXml = default_strings_xml; PackageName = PackageName ?? string.Format ("{0}.{0}", ProjectName); + JavaPackageName = JavaPackageName ?? PackageName.ToLowerInvariant (); OtherBuildItems.Add (new BuildItem.NoActionResource ("Properties\\AndroidManifest.xml") { TextContent = () => AndroidManifest.Replace("${PROJECT_NAME}", ProjectName). @@ -146,7 +147,8 @@ public AndroidLinkMode AndroidLinkModeRelease { public string MainActivity { get; set; } public string StringsXml { get; set; } public string PackageName { get; set; } - + public string JavaPackageName { get; set; } + public override BuildOutput CreateBuildOutput (ProjectBuilder builder) { return new AndroidApplicationBuildOutput (this) { Builder = builder }; @@ -156,5 +158,13 @@ public void SetDefaultTargetDevice () { SetProperty ("AdbTarget", Environment.GetEnvironmentVariable ("ADB_TARGET")); } + + public override string ProcessSourceTemplate (string source) + { + return source.Replace ("${ROOT_NAMESPACE}", RootNamespace ?? ProjectName) + .Replace ("${PROJECT_NAME}", ProjectName) + .Replace ("${PACKAGENAME}", PackageName) + .Replace ("${JAVA_PACKAGENAME}", JavaPackageName); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DownloadedCache.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DownloadedCache.cs index 500f3ae00ab..298a7a8210b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DownloadedCache.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DownloadedCache.cs @@ -24,27 +24,19 @@ public DownloadedCache (string cacheDirectory) public string CacheDirectory { get; private set; } - public string GetAsFile (string url, string md5 = null) + public string GetAsFile (string url) { Directory.CreateDirectory (CacheDirectory); var filename = Path.Combine (CacheDirectory, Path.GetFileName (new Uri (url).LocalPath)); lock (locks.GetOrAdd (filename, _ => new object ())) { - if (File.Exists (filename) && (md5 == null || GetMd5 (filename) == md5)) + if (File.Exists (filename)) return filename; // FIXME: should be clever enough to resolve name conflicts. new System.Net.WebClient ().DownloadFile (url, filename); - if (md5 != null && GetMd5 (filename) != md5) - throw new InvalidOperationException (string.Format ("The given md5sum doesn't match the actual web resource. '{0}' for '{1}'", md5, url)); return filename; } } - - string GetMd5 (string filename) - { - var md5 = MD5.Create (); - return new string (md5.ComputeHash (File.ReadAllBytes (filename)).Select (b => (char) (b < 10 ? '0' + b : 'a' + b - 10)).ToArray ()); - } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs index 7e67fd9fb1a..f109ff14e5d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs @@ -350,7 +350,7 @@ public virtual void NuGetRestore (string directory, string packagesDirectory = n } } - public string ProcessSourceTemplate (string source) + public virtual string ProcessSourceTemplate (string source) { return source.Replace ("${ROOT_NAMESPACE}", RootNamespace ?? ProjectName).Replace ("${PROJECT_NAME}", ProjectName); } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/MainActivity.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/MainActivity.cs index 5e6bc5c069a..04a91ffc8e6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/MainActivity.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/MainActivity.cs @@ -9,7 +9,7 @@ namespace ${ROOT_NAMESPACE} { - [Activity (Label = "${PROJECT_NAME}", MainLauncher = true, Icon = "@drawable/icon")] + [Register ("${JAVA_PACKAGENAME}.MainActivity"), Activity (Label = "${PROJECT_NAME}", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { int count = 1; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainActivity.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainActivity.cs index 800f1dd33a9..718c868b0f0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainActivity.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Forms/MainActivity.cs @@ -9,7 +9,7 @@ namespace ${ROOT_NAMESPACE} { - [Activity (Label = "${PROJECT_NAME}", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] + [Register("${JAVA_PACKAGENAME}.MainActivity"), Activity(Label = "${PROJECT_NAME}", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate (Bundle savedInstanceState) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Wear/MainActivity.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Wear/MainActivity.cs index 8f06b878a68..7fb0cd749e9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Wear/MainActivity.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Wear/MainActivity.cs @@ -11,7 +11,7 @@ namespace ${ROOT_NAMESPACE} { - [Activity (Label = "${PROJECT_NAME}", MainLauncher = true, Icon = "@drawable/icon")] + [Register("${JAVA_PACKAGENAME}.MainActivity"), Activity(Label = "${PROJECT_NAME}", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { int count = 1; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs index 749886e0ce7..d4b9d505923 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs @@ -1,17 +1,19 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; - -using Xamarin.Tools.Zip; -using System.Collections.Generic; using System.Text; +using Java.Interop.Tools.JavaCallableWrappers; +using Xamarin.Tools.Zip; + #if MSBUILD using Microsoft.Build.Utilities; using Xamarin.Android.Tasks; #endif -namespace Xamarin.Android.Tools { +namespace Xamarin.Android.Tools +{ static class Files { @@ -365,15 +367,15 @@ public static string HashString (string s) public static string HashBytes (byte [] bytes) { - using (HashAlgorithm hashAlg = new SHA1Managed ()) { + using (HashAlgorithm hashAlg = new Crc64 ()) { byte [] hash = hashAlg.ComputeHash (bytes); - return BitConverter.ToString (hash); + return ToHexString (hash); } } public static string HashFile (string filename) { - using (HashAlgorithm hashAlg = new SHA1Managed ()) { + using (HashAlgorithm hashAlg = new Crc64 ()) { return HashFile (filename, hashAlg); } } @@ -382,7 +384,7 @@ public static string HashFile (string filename, HashAlgorithm hashAlg) { using (Stream file = new FileStream (filename, FileMode.Open, FileAccess.Read)) { byte[] hash = hashAlg.ComputeHash (file); - return BitConverter.ToString (hash); + return ToHexString (hash); } } @@ -390,12 +392,25 @@ public static string HashStream (Stream stream) { stream.Position = 0; - using (HashAlgorithm hashAlg = new SHA1Managed ()) { + using (HashAlgorithm hashAlg = new Crc64 ()) { byte[] hash = hashAlg.ComputeHash (stream); - return BitConverter.ToString (hash); + return ToHexString (hash); } } + public static string ToHexString (byte[] hash) + { + char [] array = new char [hash.Length * 2]; + for (int i = 0, j = 0; i < hash.Length; i += 1, j += 2) { + byte b = hash [i]; + array [j] = GetHexValue (b / 16); + array [j + 1] = GetHexValue (b % 16); + } + return new string (array); + } + + static char GetHexValue (int i) => (char) (i < 10 ? i + 48 : i - 10 + 65); + public static void DeleteFile (string filename, object log) { try { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index e64ce4a317a..3036e454bac 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -470,6 +470,11 @@ public static string HashStream (Stream stream) return Files.HashStream (stream); } + public static bool HasFileChanged (string source, string destination) + { + return Files.HasFileChanged (source, destination); + } + /// /// Open a file given its path and remove the 3 bytes UTF-8 BOM if there is one /// diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyDataStream.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyDataStream.cs index af8f72deefa..f097fc64f87 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyDataStream.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyDataStream.cs @@ -3,6 +3,7 @@ using System.Security.Cryptography; using System.IO; using System.Text; +using Java.Interop.Tools.JavaCallableWrappers; namespace Xamarin.Android.Tasks { @@ -25,7 +26,7 @@ class NativeAssemblyDataStream : Stream StreamWriter outputWriter; bool firstWrite = true; uint rowFieldCounter; - SHA1CryptoServiceProvider sha1; + HashAlgorithm hashAlgorithm; bool hashingFinished; uint byteCount = 0; @@ -49,18 +50,18 @@ public NativeAssemblyDataStream () { outputStream = new MemoryStream (); outputWriter = new StreamWriter (outputStream, new UTF8Encoding (false)); - sha1 = new SHA1CryptoServiceProvider (); - sha1.Initialize (); + hashAlgorithm = new Crc64 (); + hashAlgorithm.Initialize (); } public byte[] GetStreamHash () { if (!hashingFinished) { - sha1.TransformFinalBlock (new byte[0], 0, 0); + hashAlgorithm.TransformFinalBlock (new byte[0], 0, 0); hashingFinished = true; } - return sha1.Hash; + return hashAlgorithm.Hash; } protected override void Dispose (bool disposing) @@ -115,7 +116,7 @@ public override void Write (byte[] buffer, int offset, int count) throw new ObjectDisposedException (this.GetType ().Name); if (!hashingFinished) - sha1.TransformBlock (buffer, offset, count, null, 0); + hashAlgorithm.TransformBlock (buffer, offset, count, null, 0); if (firstWrite) { // Kind of a backward thing to do, but I wanted to avoid having to modfy (and bump the diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyGenerator.cs index a4154849495..17bfca35db7 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeAssemblyGenerator.cs @@ -34,14 +34,6 @@ protected virtual void WriteSymbols (StreamWriter output) { } - protected string HashToHex (byte[] dataHash) - { - var sb = new StringBuilder (); - foreach (byte b in dataHash) - sb.Append ($"{b:x02}"); - return sb.ToString (); - } - protected void WriteEndLine (StreamWriter output, string comment = null, bool indent = true) { if (!String.IsNullOrEmpty (comment)) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs index e85f7db6da1..6b2d36f109b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingNativeAssemblyGenerator.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using System.Text; +using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks { @@ -33,8 +33,8 @@ protected override void WriteFileHeader (StreamWriter output, string outputFileN // actually different whenever the data changes. Relying on mapping header values for this // purpose would not be enough since the only change to the mapping might be a single-character // change in one of the type names and we must be sure the assembly is rebuilt in all cases, - // thus the SHA1. - WriteEndLine (output, $"Data SHA1: {HashToHex (dataStream.GetStreamHash ())}", false); + // thus the hash. + WriteEndLine (output, $"Data Hash: {Files.ToHexString (dataStream.GetStreamHash ())}", false); base.WriteFileHeader (output, outputFileName); } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in index 1a59ad3ed33..c03391c09b3 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in @@ -12,6 +12,7 @@ {abi}{versionCode:D5} UpdateGeneratedFiles True + LowercaseCrc64 False 28.0.3 28.0.0 diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index d974a3299d9..169a061384e 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -309,10 +309,8 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. True False False - md5withRSA - SHA256withRSA - SHA1 - SHA-256 + SHA256withRSA + SHA-256 1G @@ -630,6 +628,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. _CheckNonIdealConfigurations; _SetupDesignTimeBuildForBuild; _CleanIntermediateIfNuGetsChange; + _CleanIntermediateIfPackageNamingPolicyChanges; _CreatePropertiesCache; _CheckProjectItems; _CheckForContent; @@ -649,6 +648,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. _CheckNonIdealConfigurations; _SetupDesignTimeBuildForBuild; _CleanIntermediateIfNuGetsChange; + _CleanIntermediateIfPackageNamingPolicyChanges; _CreatePropertiesCache; _AddAndroidDefines; _CreateNativeLibraryArchive; @@ -790,6 +790,23 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + + + + + + + + + @@ -1270,6 +1287,7 @@ because xbuild doesn't support framework reference assemblies. <_PropertyCacheItems Include="OS=$(OS)" /> <_PropertyCacheItems Include="DesignTimeBuild=$(DesignTimeBuild)" /> <_PropertyCacheItems Include="AndroidIncludeDebugSymbols=$(AndroidIncludeDebugSymbols)" /> + <_PropertyCacheItems Include="AndroidPackageNamingPolicy=$(AndroidPackageNamingPolicy)" /> - - - - - - - - - + + + <_GeneratedAndroidEnvironment Include="__XA_PACKAGE_NAMING_POLICY__=$(AndroidPackageNamingPolicy)" /> + <_GeneratedAndroidEnvironment Include="mono.enable_assembly_preload=0" Condition=" '$(AndroidEnablePreloadAssemblies)' != 'True' " /> + + + + + + + <_GeneratePackageManagerJavaDependsOn> @@ -2262,7 +2281,7 @@ because xbuild doesn't support framework reference assemblies. $(_AfterAddStaticResources); _PrepareAssemblies; _PrepareEnvironmentAssemblySources; - _SetupAssemblyPreload; + _GenerateEnvironmentFiles; diff --git a/src/Xamarin.Android.Tools.JavadocImporter/Xamarin.Android.Tools.JavadocImporter.csproj b/src/Xamarin.Android.Tools.JavadocImporter/Xamarin.Android.Tools.JavadocImporter.csproj index cb8a50cd2ad..9be294b04b2 100644 --- a/src/Xamarin.Android.Tools.JavadocImporter/Xamarin.Android.Tools.JavadocImporter.csproj +++ b/src/Xamarin.Android.Tools.JavadocImporter/Xamarin.Android.Tools.JavadocImporter.csproj @@ -62,6 +62,9 @@ StringRocks.cs + + Crc64.cs + diff --git a/src/Xamarin.Android.Tools.JavadocImporter/samples.cs b/src/Xamarin.Android.Tools.JavadocImporter/samples.cs index 964b4194518..6fd5209c001 100644 --- a/src/Xamarin.Android.Tools.JavadocImporter/samples.cs +++ b/src/Xamarin.Android.Tools.JavadocImporter/samples.cs @@ -5,6 +5,7 @@ using System.Security.Cryptography; using System.Text; using System.Xml.Serialization; +using Java.Interop.Tools.JavaCallableWrappers; using Xamarin.Tools.Zip; public struct SampleDesc { @@ -22,7 +23,7 @@ public override string ToString () public class SampleRepository { ZipArchive archive; - HashAlgorithm hasher = MD5.Create (); + HashAlgorithm hasher = new Crc64 (); HashSet validFiles = new HashSet (); Dictionary index = new Dictionary (); Dictionary updates = new Dictionary (); diff --git a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj index 3fc47536b54..2373b4de1ea 100644 --- a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj +++ b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj @@ -59,6 +59,10 @@ + + {3F1F2F50-AF1A-4A5A-BEDB-193372F068D7} + Xamarin.Android.Build.Tasks + {2DD1EE75-6D8D-4653-A800-0A24367F7F38} Xamarin.ProjectTools diff --git a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs index fe4e845d801..590189ea79b 100644 --- a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs @@ -52,7 +52,7 @@ public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isReleas if (CommercialBuildAvailable) Assert.True (b.RunTarget (proj, "_Run"), "Project should have run."); else - AdbStartActivity ($"{proj.PackageName}/md52d9cf6333b8e95e8683a477bc589eda5.MainActivity"); + AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity"); Assert.True (WaitForActivityToStart (proj.PackageName, "MainActivity", Path.Combine (Root, b.ProjectDirectory, "logcat.log"), 30), "Activity should have started."); diff --git a/tests/MSBuildDeviceIntegration/Tests/DeploymentTest.cs b/tests/MSBuildDeviceIntegration/Tests/DeploymentTest.cs index 2a41225bfc3..6da5f6dd0dc 100644 --- a/tests/MSBuildDeviceIntegration/Tests/DeploymentTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/DeploymentTest.cs @@ -70,7 +70,7 @@ public void CheckXamarinFormsAppDeploysAndAButtonWorks () { if (!HasDevices) Assert.Ignore ("Skipping Test. No devices available."); - AdbStartActivity ($"{proj.PackageName}/md52d9cf6333b8e95e8683a477bc589eda5.MainActivity"); + AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity"); WaitForActivityToStart (proj.PackageName, "MainActivity", Path.Combine (Root, builder.ProjectDirectory, "startup-logcat.log"), 15); ClearAdbLogcat (); @@ -110,7 +110,7 @@ public void CheckTimeZoneInfoIsCorrect (string timeZone) try { RunAdbCommand ($"shell su root setprop persist.sys.timezone \"{timeZone}\""); ClearAdbLogcat (); - AdbStartActivity ($"{proj.PackageName}/md52d9cf6333b8e95e8683a477bc589eda5.MainActivity"); + AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity"); Assert.IsTrue (WaitForActivityToStart (proj.PackageName, "MainActivity", Path.Combine (Root, builder.ProjectDirectory, "startup-logcat.log")), "Activity should have started"); Assert.IsTrue (MonitorAdbLogcat ((l) => { diff --git a/tools/msbuild-fuzzer/Program.cs b/tools/msbuild-fuzzer/Program.cs index d3d9e7c3fec..fc912f355d1 100644 --- a/tools/msbuild-fuzzer/Program.cs +++ b/tools/msbuild-fuzzer/Program.cs @@ -135,7 +135,7 @@ static bool Run () Console.WriteLine (nameof (Run)); string stop = Adb ($"shell am force-stop {application.PackageName}", ignoreExitCode: true); Adb ("logcat -c"); - string activity = $"{application.PackageName}/md52d9cf6333b8e95e8683a477bc589eda5.MainActivity"; + string activity = $"{application.PackageName}/{application.JavaPackageName}.MainActivity"; string start = Adb ($"shell am start -n {activity}"); Console.WriteLine (start.Trim ()); //Wait for the app to start