Skip to content

Commit a76edb8

Browse files
authored
[Xamarin.Android.Tools.ApiXmlAdjuster] Find app.android.IntentService (#718)
Fixes: #717 A long time ago (2011-Jan-28), [Novell Bug #655342][0] was filed: the following fragment was expected to work, but didn't: // C# [Service] class MyService : Android.App.IntentService { public MyService() { } protected override void OnHandleIntent(Intent intent) { } } Why didn't it work? Two reasons: 1. `android.app.IntentService` has no default constructor; it only has an [`IntentService(String)`][1] constructor. 2. In 2011, there was no way for a C# type to provide constructor parameters to the Java constructor. The result is that the Java Callable Wrapper for `MyService` was not valid Java code, as it attempted to use a non-existent constructor: // Java /* partial */ class MyService extends android.app.IntentService { public MyService() { super(); // javac error; no such constructor exists // … } } Note that there's no way to make `MyService() : base("name")` work, which would be the "obvious" solution, unless `jcw-gen` contains an IL interpreter to determine what parameters were provided to the base class constructor, and…no. Too complicated. Not happening. Note: (2) later got "solved" [in 2013][2] by adding the [`ExportAttribute.SuperArgumentsString` property][3], which would allow: [Service] partial class MyService : Android.App.IntentService { [Export(SuperArgumentsString="\"name\"")] public MyService() : base ("name") { } } --End note. Lacking a time machine, [the 2011 fix][4] was to "fudge" it: 1. Add a new [`mono.android.app.IntentService`][5] Java type which contains a default constructor. 2. ["Rename"][6] `android.app.IntentService` as `mono.android.app.IntentService`. That way, the original C# declaration would generate a Java Callable Wrapper resembling: // Java /* partial */ class MyService extends mono.android.app.IntentService { public MyService() { super(); // now valid; it exists! // … } } Unfortunately, this doesn't properly address *bindings*: what happens if a *Java* type inherits `android.app.IntentService`, as is the case in [`Microsoft.Intune.MAM.SDK.aar`][7], and that type is bound? In the original `jar2xml` world order, it worked: [`jar2xml`][8] is a Java app, and for it to run `$CLASSPATH` needed to contain `android.jar`, which *did* have `android.app.IntentService`. Thus, all required types could be resolved. In the new `class-parse` world order, this isn't the case: `class-parse` directly reads Java bytecode; no JVM is used. The result is that binding projects which move from `jar2xml` to `class-parse` could now get a new error: Error while processing type '[Class] com.microsoft.intune.mam.client.app.MAMIntentService': Type 'android.app.IntentService' was not found. `android.app.IntentService` wasn't found because `Mono.Android.dll` doesn't contain a binding for that type; it only binds `mono.android.app.IntentService`. The fix for this unfortunate state of affairs is Yet Another Kludge to maintain backward compatibility: update `JavaApiTypeResolverExtensions.FindNonGenericType()` so that it knows about the 2011 kludge: if `android.app.IntentService` is requested, hand it back the `JavaType` instance for `mono.android.app.IntentService`. [0]: http://bugzilla.novell.com/show_bug.cgi?id=655342 [1]: https://developer.android.com/reference/android/app/IntentService#IntentService(java.lang.String) [2]: xamarin/monodroid@a6c3497 [3]: https://docs.microsoft.com/en-us/dotnet/api/java.interop.exportattribute.superargumentsstring [4]: xamarin/monodroid@7a42b46a39 [5]: https://github.com/xamarin/xamarin-android/blob/a7bda88d05b58efd26c17be3956c7140d05868e1/src/Mono.Android/java/mono/android/app/IntentService.java [6]: https://github.com/xamarin/xamarin-android/blob/a7bda88d05b58efd26c17be3956c7140d05868e1/src/Mono.Android/metadata#L570-L583 [7]: https://github.com/msintuneappsdk/ms-intune-app-sdk-android/blob/master/Microsoft.Intune.MAM.SDK.aar [8]: https://github.com/xamarin/jar2xml
1 parent 6cde087 commit a76edb8

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,15 @@ public static JavaType FindNonGenericType (this JavaApi api, string name)
4444
ret = ManagedType.DummyManagedPackages
4545
.SelectMany (p => p.Types)
4646
.FirstOrDefault (t => t.FullName == name);
47-
if (ret == null)
47+
if (ret == null) {
48+
// We moved this type to "mono.android.app.IntentService" which makes this
49+
// type resolution fail if a user tries to reference it in Java.
50+
if (name == "android.app.IntentService")
51+
return FindNonGenericType (api, "mono.android.app.IntentService");
52+
4853
throw new JavaTypeResolutionException (string.Format ("Type '{0}' was not found.", name));
54+
}
55+
4956
return ret;
5057
}
5158

tests/Xamarin.Android.Tools.ApiXmlAdjuster-Tests/JavaApiTestHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class JavaApiTestHelper
1111
"..",
1212
"..");
1313

14-
static readonly string ApiPath = Path.Combine (
14+
public static readonly string ApiPath = Path.Combine (
1515
TopDir,
1616
"tests",
1717
"Xamarin.Android.Tools.ApiXmlAdjuster-Tests",

tests/Xamarin.Android.Tools.ApiXmlAdjuster-Tests/TypeResolverTest.cs

+32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
24
using System.Linq;
5+
using System.Xml;
36
using NUnit.Framework;
47

58
namespace Xamarin.Android.Tools.ApiXmlAdjuster.Tests
@@ -65,6 +68,35 @@ public void ResolveGenericArguments ()
6568
para.ResolvedType.ToString (),
6669
"referenced type is not correctly resolved");
6770
}
71+
72+
[Test]
73+
public void IntentServiceHack ()
74+
{
75+
// https://github.com/xamarin/java.interop/issues/717
76+
var api = JavaApiTestHelper.GetLoadedApi ();
77+
78+
// Create "mono.android.app" package
79+
var mono_android_app = new JavaPackage (api) { Name = "mono.android.app", JniName = "mono/android/app", Types = new List<JavaType> () };
80+
api.Packages.Add (mono_android_app);
81+
82+
// Remove "android.app.IntentService" type
83+
var android_app = api.Packages.Single (p => p.Name == "android.app");
84+
var intent_service = android_app.Types.Single (t => t.Name == "IntentService");
85+
android_app.Types.Remove (intent_service);
86+
87+
// Create new "mono.android.app.IntentService" type
88+
var new_intent_service = new JavaClass (mono_android_app) {
89+
Name = intent_service.Name,
90+
};
91+
92+
mono_android_app.Types.Add (new_intent_service);
93+
94+
api.Resolve ();
95+
96+
// Ensure we can resolve the type by either name
97+
Assert.AreSame (new_intent_service, api.FindNonGenericType ("android.app.IntentService"));
98+
Assert.AreSame (new_intent_service, api.FindNonGenericType ("mono.android.app.IntentService"));
99+
}
68100
}
69101
}
70102

0 commit comments

Comments
 (0)