diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs
new file mode 100644
index 00000000000..b4bb144001a
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Java.Interop.Tools.Cecil;
+using Microsoft.Build.Utilities;
+using Mono.Cecil;
+using NUnit.Framework;
+using Xamarin.Android.Tasks;
+
+namespace Xamarin.Android.Build.Tests;
+
+///
+/// In-process tests for how merges the computed
+/// minSdkVersion/targetSdkVersion into a user-authored <uses-sdk/> element.
+///
+[TestFixture]
+[Parallelizable (ParallelScope.Self)]
+public class ManifestDocumentTest : BaseTest
+{
+ const string DefaultMinSdkVersion = "21";
+ const string DefaultTargetSdkVersion = "37";
+
+ static readonly XNamespace AndroidNs = "http://schemas.android.com/apk/res/android";
+
+ static string CreateManifest (string usesSdk) => $@"
+
+ {usesSdk}
+
+
+";
+
+ static XElement MergeAndGetUsesSdk (string usesSdk, out XDocument document)
+ {
+ var templateFile = Path.GetTempFileName ();
+ try {
+ File.WriteAllText (templateFile, CreateManifest (usesSdk));
+
+ var manifest = new ManifestDocument (templateFile) {
+ PackageName = "com.xamarin.usessdk",
+ TargetSdkVersion = DefaultTargetSdkVersion,
+ MinSdkVersion = DefaultMinSdkVersion,
+ VersionResolver = new MockVersionResolver {
+ GetApiLevelFromIdFunc = id => int.TryParse (id, out var apiLevel) ? (int?) apiLevel : null,
+ GetIdFromApiLevelFunc = apiLevel => apiLevel,
+ },
+ };
+
+ var engine = new MockBuildEngine (TestContext.Out);
+ var log = new TaskLoggingHelper (engine, nameof (ManifestDocumentTest));
+ manifest.Merge (log, new TypeDefinitionCache (), new List (), applicationClass: null, embed: false, bundledWearApplicationName: null, mergedManifestDocuments: null);
+
+ var writer = new StringWriter ();
+ manifest.Save ((code, message) => { }, writer);
+
+ document = XDocument.Parse (writer.ToString ());
+ var element = document.Root.Element ("uses-sdk");
+ Assert.IsNotNull (element, "uses-sdk element should exist");
+ return element;
+ } finally {
+ File.Delete (templateFile);
+ }
+ }
+
+ static void AssertUsesSdk (string usesSdk, string expectedMinSdkVersion, string expectedTargetSdkVersion)
+ {
+ var element = MergeAndGetUsesSdk (usesSdk, out var document);
+
+ Assert.AreEqual (expectedMinSdkVersion, element.Attribute (AndroidNs + "minSdkVersion")?.Value, "unexpected android:minSdkVersion");
+ Assert.AreEqual (expectedTargetSdkVersion, element.Attribute (AndroidNs + "targetSdkVersion")?.Value, "unexpected android:targetSdkVersion");
+
+ foreach (var comment in document.DescendantNodes ().OfType ()) {
+ StringAssert.DoesNotContain ("UsesMinSdkAttributes", comment.Value, "the lint suppression comment should no longer be emitted");
+ }
+ }
+
+ [Test]
+ public void NoUsesSdkElement ()
+ {
+ AssertUsesSdk ("", DefaultMinSdkVersion, DefaultTargetSdkVersion);
+ }
+
+ [Test]
+ public void MinSdkVersionOnly ()
+ {
+ // Regression test: targetSdkVersion was never written, so `aapt2` defaulted it to minSdkVersion.
+ AssertUsesSdk (@"", "24", DefaultTargetSdkVersion);
+ }
+
+ [Test]
+ public void TargetSdkVersionOnly ()
+ {
+ AssertUsesSdk (@"", DefaultMinSdkVersion, "30");
+ }
+
+ [Test]
+ public void MinAndTargetSdkVersion ()
+ {
+ AssertUsesSdk (@"", "24", "30");
+ }
+}
+
+class MockVersionResolver : IVersionResolver
+{
+ public Func GetApiLevelFromIdFunc { get; set; } = _ => 99;
+ public Func GetIdFromApiLevelFunc { get; set; } = _ => "API-99";
+
+ public int? GetApiLevelFromId (string id) => GetApiLevelFromIdFunc (id);
+
+ public string GetIdFromApiLevel (string apiLevel) => GetIdFromApiLevelFunc (apiLevel);
+}
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 122208b3356..0f5d6141d80 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
@@ -1728,16 +1728,6 @@ public void TargetSdkVersion_361In_36Out ()
File.Delete (templateFile);
}
}
-
- class MockVersionResolver : IVersionResolver
- {
- public Func GetApiLevelFromIdFunc { get; set; } = _ => 99;
- public Func GetIdFromApiLevelFunc { get; set; } = _ => "API-99";
-
- public int? GetApiLevelFromId (string id) => GetApiLevelFromIdFunc (id);
-
- public string GetIdFromApiLevel (string apiLevel) => GetIdFromApiLevelFunc (apiLevel);
- }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
index ba9b5b878f8..5b040d4a458 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
@@ -324,13 +324,14 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li
uses.SetAttributeValue (androidNs + "minSdkVersion", minSdkVersionString);
}
+ // If no targetSdkVersion is specified, set it. Otherwise `aapt2` defaults it to minSdkVersion.
string targetSdkVersion;
var tsv = uses.Attribute (androidNs + "targetSdkVersion");
if (tsv != null)
targetSdkVersion = tsv.Value;
else {
targetSdkVersion = TargetSdkVersionName;
- uses.AddBeforeSelf (new XComment ("suppress UsesMinSdkAttributes"));
+ uses.SetAttributeValue (androidNs + "targetSdkVersion", targetSdkVersion);
}
int? tryTargetSdkVersion = VersionResolver.GetApiLevelFromId (targetSdkVersion);