Skip to content

Commit 611c269

Browse files
[Microsoft.Android.Sdk.ILLink] preserve types with IJniNameProviderAttribute (#9099)
Fixes: #8940 Context: TobiasBuchholz/Plugin.Firebase#144 Using the NuGet package: <PackageReference Include="Plugin.Firebase.CloudMessaging" Version="3.0.0" /> Includes a service: namespace Plugin.Firebase.CloudMessaging.Platforms.Android; [Service(Exported = true)] [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })] public class MyFirebaseMessagingService : FirebaseMessagingService Unfortunately, using `TrimMode=full` completely trims away the above service, which is required for push notifications to work. I could reproduce this problem in a test using the above NuGet package. To fix this, we can modify `MarkJavaObjects` to preserve types with attributes that implement `Java.Interop.IJniNameProviderAttribute`, and the new test now passes. With one exception, `Android.Runtime.RegisterAttribute`, should not be preserved as that would be any Java type bound for C#.
1 parent 83626c9 commit 611c269

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
using Mono.Cecil;
55
using Mono.Linker;
66
using Mono.Linker.Steps;
7-
using Mono.Tuner;
87
using Java.Interop.Tools.Cecil;
98
using Xamarin.Android.Tasks;
9+
using Profile = Microsoft.Android.Sdk.ILLink.Profile;
1010

1111
namespace MonoDroid.Tuner {
1212

@@ -27,7 +27,14 @@ public override void Initialize (LinkContext context, MarkContext markContext)
2727

2828
bool IsActiveFor (AssemblyDefinition assembly)
2929
{
30-
return assembly.MainModule.HasTypeReference ("System.Net.Http.HttpMessageHandler") || assembly.MainModule.HasTypeReference ("Android.Util.IAttributeSet");
30+
if (Profile.IsSdkAssembly (assembly))
31+
return false;
32+
if (Profile.IsProductAssembly (assembly))
33+
return false;
34+
35+
return assembly.MainModule.HasTypeReference ("System.Net.Http.HttpMessageHandler") ||
36+
assembly.MainModule.HasTypeReference ("Java.Lang.Object") ||
37+
assembly.MainModule.HasTypeReference ("Android.Util.IAttributeSet");
3138
}
3239

3340
public void ProcessAssembly (AssemblyDefinition assembly, string androidHttpClientHandlerType, Dictionary<string, HashSet<string>> customViewMap)
@@ -47,14 +54,42 @@ public void ProcessAssembly (AssemblyDefinition assembly, string androidHttpClie
4754
}
4855
}
4956

50-
// Custom views in Android .xml files
57+
// Continue if not an IJavaObject
5158
if (!type.ImplementsIJavaObject (cache))
5259
continue;
60+
61+
// Custom views in Android .xml files
5362
if (customViewMap.ContainsKey (type.FullName)) {
5463
Annotations.Mark (type);
5564
PreserveJavaObjectImplementation (type);
65+
continue;
66+
}
67+
68+
// Types with Java.Interop.IJniNameProviderAttribute attributes
69+
if (ShouldPreserveBasedOnAttributes (type)) {
70+
Annotations.Mark (type);
71+
PreserveJavaObjectImplementation (type);
72+
continue;
73+
}
74+
}
75+
}
76+
77+
bool ShouldPreserveBasedOnAttributes (TypeDefinition type)
78+
{
79+
if (!type.HasCustomAttributes)
80+
return false;
81+
82+
foreach (var attr in type.CustomAttributes) {
83+
// Ignore Android.Runtime.RegisterAttribute
84+
if (attr.AttributeType.FullName == "Android.Runtime.RegisterAttribute") {
85+
continue;
86+
}
87+
if (attr.AttributeType.Implements ("Java.Interop.IJniNameProviderAttribute", cache)) {
88+
return true;
5689
}
5790
}
91+
92+
return false;
5893
}
5994

6095
public void ProcessType (TypeDefinition type)

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,40 @@ public void PreserveIX509TrustManagerSubclasses ([Values(true, false)] bool hasS
598598
}
599599
}
600600

601+
[Test]
602+
public void PreserveServices ()
603+
{
604+
var proj = new XamarinAndroidApplicationProject {
605+
IsRelease = true,
606+
TrimModeRelease = TrimMode.Full,
607+
PackageReferences = {
608+
new Package { Id = "Plugin.Firebase.CloudMessaging", Version = "3.0.0" },
609+
}
610+
};
611+
proj.MainActivity = proj.DefaultMainActivity
612+
.Replace ("//${FIELDS}",
613+
"""
614+
protected override void OnNewIntent (Android.Content.Intent? intent)
615+
{
616+
base.OnNewIntent (intent);
617+
Plugin.Firebase.CloudMessaging.FirebaseCloudMessagingImplementation.OnNewIntent (intent);
618+
}
619+
""")
620+
.Replace ("//${AFTER_ONCREATE}", "Plugin.Firebase.Core.Platforms.Android.CrossFirebase.Initialize (this);");
621+
622+
using var b = CreateApkBuilder ();
623+
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
624+
625+
var assemblyPath = BuildTest.GetLinkedPath (b, isRelease: true, "Plugin.Firebase.CloudMessaging.dll");
626+
FileAssert.Exists (assemblyPath);
627+
using var assembly = AssemblyDefinition.ReadAssembly (assemblyPath);
628+
var types = new [] { "Plugin.Firebase.CloudMessaging.Platforms.Android.MyFirebaseMessagingService" };
629+
foreach (var typeName in types) {
630+
var td = assembly.MainModule.GetType (typeName);
631+
Assert.IsNotNull (td, $"{typeName} should not have been linked out!");
632+
}
633+
}
634+
601635
[Test]
602636
public void DoNotErrorOnPerArchJavaTypeDuplicates ([Values(true, false)] bool enableMarshalMethods)
603637
{

0 commit comments

Comments
 (0)