From 58a81eb148acb2d2f5d3df91629bba3a3129e773 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 11 Oct 2023 23:09:36 +0100 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Fix up ForegroundService.ToString() (#8412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/xamarin-android/issues/8409 Context: https://github.com/xamarin/xamarin-android/issues/8272 The [`//service/@android:foregroundServiceType`][0] attribute can be generated based on the value of the `Android.App.ServiceAttribute.ForegroundServiceType` property: [Service(ForegroundServiceType=ForegroundService.TypeCamera)] partial class MyService : Service { } which will result in an `AndroidManifest.xml` fragment such as: However, a number of `ForegroundService` enum values have been added without corresponding updates to `ServiceAttribute` XML generation. Consequently, using "recently added" values such as `ForegroundService.TypeHealth` would result in those values *not* being added to the generated `//service/@android:foregroundServiceType`. Update `ManifestDocumentElement.cs` to update `ToString(ForegroundService)` so that all current `ForegroundService` enum values are supported. This will allow: [Service(ForegroundServiceType= ForegroundService.TypeCamera | // previously supported ForegroundService.TypeMicrophone)] // new hawtness partial class MyService : Service { } to properly emit: [0]: https://developer.android.com/guide/topics/manifest/service-element#foregroundservicetype --- .../ManifestTest.cs | 27 +++++++++++++++++++ .../Utilities/BaseTest.cs | 2 +- .../Utilities/ManifestDocumentElement.cs | 22 ++++++++++++--- 3 files changed, 46 insertions(+), 5 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 c9fe8ffb548..fdb987dbf5b 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 @@ -908,6 +908,33 @@ class TestActivity : Activity { }" } } + [Test] + [TestCase ("Android.Content.PM.ForegroundService.TypeSpecialUse", "specialUse")] + [TestCase ("Android.Content.PM.ForegroundService.TypeConnectedDevice", "connectedDevice")] + [TestCase ("Android.Content.PM.ForegroundService.TypeCamera|Android.Content.PM.ForegroundService.TypeMicrophone", "camera|microphone")] + public void AllForegroundServiceTypes (string serviceType, string expected) + { + var proj = new XamarinAndroidApplicationProject { + }; + + proj.Sources.Add (new BuildItem.Source ("TestActivity.cs") { + TextContent = () => $@"using Android.App; + using Android.Content.PM; + using Android.Views; + [Service (ForegroundServiceType = {serviceType})] + class TestService : Service {{ public override Android.OS.IBinder OnBind (Android.Content.Intent intent) {{ return null; }} }}" + }); + using (ProjectBuilder builder = CreateApkBuilder (Path.Combine ("temp", TestName))) { + Assert.IsTrue (builder.Build (proj), "Build should have succeeded"); + string manifest = builder.Output.GetIntermediaryAsText (Path.Combine ("android", "AndroidManifest.xml")); + var doc = XDocument.Parse (manifest); + var ns = XNamespace.Get ("http://schemas.android.com/apk/res/android"); + IEnumerable services = doc.Element ("manifest")?.Element ("application")?.Elements ("service"); + XElement e = services.FirstOrDefault (x => x.Attribute (ns.GetName ("foregroundServiceType"))?.Value == expected); + Assert.IsNotNull (e, $"Manifest should contain an service with a foregroundServiceType of {expected}"); + } + } + [Test] public void AllServiceAttributeProperties ([Values ("legacy", "manifestmerger.jar")] string manifestMerger) { 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 f6e2af09cc3..ec66ffc55cd 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 @@ -45,7 +45,7 @@ public void AssertCommercialBuild (bool fail = false) } } - char [] invalidChars = { '{', '}', '(', ')', '$', ':', ';', '\"', '\'', ',', '=' }; + char [] invalidChars = { '{', '}', '(', ')', '$', ':', ';', '\"', '\'', ',', '=', '|' }; public string TestName { get { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs index 959626874db..6b685a996c9 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocumentElement.cs @@ -383,12 +383,26 @@ static string ToString (ForegroundService value) values.Add ("mediaProjection"); if (value.HasFlag (ForegroundService.TypePhoneCall)) values.Add ("phoneCall"); - - // These can be changed to enum members when API-R is the stable binding. - if (value.HasFlag ((ForegroundService)64)) + if (value.HasFlag (ForegroundService.TypeCamera)) values.Add ("camera"); - if (value.HasFlag ((ForegroundService)128)) + if (value.HasFlag (ForegroundService.TypeMicrophone)) values.Add ("microphone"); + if (value.HasFlag (ForegroundService.TypeHealth)) + values.Add ("health"); + if (value.HasFlag (ForegroundService.TypeRemoteMessaging)) + values.Add ("remoteMessaging"); + if (value.HasFlag (ForegroundService.TypeSystemExempted)) + values.Add ("systemExempted"); + if (value.HasFlag (ForegroundService.TypeShortService)) + values.Add ("shortService"); + if (value.HasFlag (ForegroundService.TypeSpecialUse)) + values.Add ("specialUse"); + + // When including a non-stable value you can cast the integer value + // to ForegroundService. We need to do this because the Build.Tasks + // only build against the latest STABLE api. + //if (value.HasFlag ((ForegroundService)128)) + // values.Add ("newValue"); return string.Join ("|", values.ToArray ()); }