From 72f28b9f88937d750720999304e2fc525f859615 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 20 Aug 2021 10:17:02 -0500 Subject: [PATCH] [Xamarin.Android.Build.Tasks] emit exported="true" for MauiLauncher activities Context: https://aster.cloud/2021/02/23/lets-be-explicit-about-our-intent-filters/ Fixes: https://github.com/xamarin/xamarin-android/issues/6196 > An important change is coming to Android 12 that improves both app > and platform security. This change affects all apps that target > Android 12. > > Activities, services, and broadcast receivers with declared > intent-filters now must explicitly declare whether they should be > exported or not. For example: Should become: Update our `AndroidManifest.xml` generation & related code so that when `MainLauncher=true`, we also automatically set `exported=true`. I updated a test to verify the contents of the generated `AndroidManifest.xml`. --- .../ManifestTest.cs | 27 ++++++++++++++++--- .../Utilities/ManifestDocument.cs | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) 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 14694bc24ce..dcc0616c828 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 @@ -141,12 +141,31 @@ public void CheckElementReOrdering ([Values (true, false)] bool useAapt2) var manifestFile = Path.Combine (Root, builder.ProjectDirectory, proj.IntermediateOutputPath, "android", "AndroidManifest.xml"); XDocument doc = XDocument.Load (manifestFile); var ns = doc.Root.GetNamespaceOfPrefix ("android"); - var manifest = doc.Element ("manifest"); - Assert.IsNotNull (manifest, "manifest element should not be null."); - var app = manifest.Element ("application"); - Assert.IsNotNull (app, "application element should not be null."); + var manifest = GetElement (doc, "manifest"); + var app = GetElement (manifest, "application"); Assert.AreEqual (0, app.ElementsAfterSelf ().Count (), "There should be no elements after the application element"); + var activity = GetElement (app, "activity"); + AssertAttribute (activity, ns + "exported", "true"); + var intent_filter = GetElement (activity, "intent-filter"); + var action = GetElement (intent_filter, "action"); + AssertAttribute (action, ns + "name", "android.intent.action.MAIN"); + var category = GetElement (intent_filter, "category"); + AssertAttribute (category, ns + "name", "android.intent.category.LAUNCHER"); + } + + static XElement GetElement (XContainer parent, XName name) + { + var e = parent.Element (name); + Assert.IsNotNull (e, $"{name} element should not be null."); + return e; + } + + static void AssertAttribute (XElement parent, XName name, string expected) + { + var a = parent.Attribute (name); + Assert.IsNotNull (a, $"{name} attribute should not be null."); + Assert.AreEqual (expected, a.Value, $"{name} attribute value did not match."); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index 8e41182cb90..b1b07d80df9 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -798,6 +798,7 @@ void AddLauncherIntentElements (XElement activity) return; var filter = new XElement ("intent-filter"); + activity.Add (new XAttribute (androidNs + "exported", "true")); activity.AddFirst (filter); foreach (KeyValuePair e in LauncherIntentElements) { if (!filter.Elements (e.Key).Any (x => ((string) x.Attribute (attName)) == e.Value))