Skip to content

Commit a2dfe8c

Browse files
dependabot[bot]simonrozsivalCopilotjonathanpeppers
authored
Bumps [external/Java.Interop](https://github.com/dotnet/java-interop) from `b881d21` to [`6820a9cb`](dotnet/java-interop@6820a9c). ## Java.Interop changes pulled in - [`6820a9c`](dotnet/java-interop@6820a9c) [Java.Interop] Make JavaProxyObject.RegisterNativeMembers private again ([#1468](dotnet/java-interop#1468)) - [`a2159d7`](dotnet/java-interop@a2159d70) Remove NuGet dependency grouping from Dependabot config ([#1459](dotnet/java-interop#1459)) - [`a0ca676`](dotnet/java-interop@a0ca6760) [ci] Update agentic workflows to claude-opus-4.8 ([#1461](dotnet/java-interop#1461)) - [`6d8aef7`](dotnet/java-interop@6d8aef7b) Bump to dotnet/android-tools@1fb68b6 ([#1457](dotnet/java-interop#1457)) - [`cfca8ad`](dotnet/java-interop@cfca8ad) Upgrade gh-aw to v0.79.6 and recompile workflows ([#1460](dotnet/java-interop#1460)) - [`0104236`](dotnet/java-interop@0104236e) [JniValueManager] Make TryConstructPeer virtual ([#1456](dotnet/java-interop#1456)) - [`d7dbad5`](dotnet/java-interop@d7dbad5) Remove Java.Interop.Dynamic ([#1449](dotnet/java-interop#1449)) - [`0244c10`](dotnet/java-interop@0244c10) Split reflection-based JniTypeManager and JniValueManager behavior ([#1441](dotnet/java-interop#1441)) - [`9ca7064`](dotnet/java-interop@9ca7064) [Java.Interop] Remove legacy `NET` preprocessor paths ([#1451](dotnet/java-interop#1451)) - [`cd2fc12`](dotnet/java-interop@cd2fc12) Remove Java.Interop.GenericMarshaler ([#1450](dotnet/java-interop#1450)) See full diff in [compare view](dotnet/java-interop@b881d21...6820a9c). ## Changes in this PR (beyond the submodule bump) The Java.Interop bump — particularly [#1441](dotnet/java-interop#1441) (split reflection-based `JniTypeManager` and `JniValueManager`) and [#1449](dotnet/java-interop#1449) (removal of `Java.Interop.Dynamic`) — required a number of follow-on changes in `dotnet/android`: ### Runtime / `Mono.Android` - **Adapt Android runtime to Java.Interop reflection managers** — switch the `JavaInteropTypeManager` / `JavaInteropValueManager` hierarchy to the new reflection-based base types and update type/value manager construction to match. - **Change base type of value managers and type managers** so the runtime composes correctly with the new Java.Interop split. - **Suppress reflection manager trim warnings only** — narrow the existing trim suppressions so they apply to the reflection-only managers and don't hide other warnings. - **Suppress IL2068 on `ManagedTypeManager.GetTypeForSimpleReference`** — annotate the entry point that legitimately requires reflection so trim analysis is satisfied. ### Tests / apkdescs - **Regenerate `BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc`** to track size changes from the Java.Interop bump. - **Regenerate Simple `BuildReleaseArm64` apkdescs** (`MonoVM` + `NativeAOT`) from a `Release` SDK so they match what CI produces. - **Update NativeAOT warning expectations** — `Mono.Android` now produces fewer trim/AOT warnings, so loosen the strict `4 Warning(s)` checks in `SupportedOSPlatformVersion` and `CheckSignApk` to `AssertHasAtMostWarnings (2)` (new helper in `AssertionExtensions`) and drop the `BuildHasTrimmerWarnings` data counts (`3 → 2`, `4 → 3`) accordingly. ### Merges - Two merges from `origin/main` to keep the branch current and resolve a conflict in `Microsoft.Android.Sdk.TypeMap.Trimmable.targets`. Co-authored-by: Simon Rozsival <simon@rozsival.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
1 parent 6d44613 commit a2dfe8c

20 files changed

Lines changed: 306 additions & 114 deletions

File tree

external/Java.Interop

Submodule Java.Interop updated 145 files

src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,15 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A
208208
continue;
209209
}
210210

211-
// [JniAddNativeMethodRegistrationAttribute] is not supported by the trimmable typemap
212-
// by design (see XA4251). Detect the attribute *before* any per-type filters below
213-
// (array type, no JNI name, etc.) so the diagnostic fires uniformly regardless of
214-
// whether the type would otherwise have ended up in the typemap.
215-
//
216-
// Skip the per-method walk entirely for the overwhelmingly common case where
217-
// the assembly doesn't even reference the attribute type — the per-assembly
218-
// flag was computed cheaply in AssemblyIndex.Build.
219-
if (index.MayUseJniAddNativeMethodRegistrationAttribute &&
220-
HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) {
221-
logger?.LogJniAddNativeMethodRegistrationAttributeError (MetadataTypeNameResolver.GetFullName (typeDef, index.Reader));
222-
}
211+
var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader);
212+
213+
// Temporarily allow [JniAddNativeMethodRegistrationAttribute] while we investigate
214+
// which scenarios fail later in the trimmable typemap pipeline.
215+
// if (index.MayUseJniAddNativeMethodRegistrationAttribute &&
216+
// !IsBuiltInJniAddNativeMethodRegistrationType (fullName, index) &&
217+
// HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) {
218+
// logger?.LogJniAddNativeMethodRegistrationAttributeError (fullName);
219+
// }
223220

224221
// Determine the JNI name and whether this is a known Java peer.
225222
// Priority:
@@ -261,8 +258,6 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A
261258
}
262259
}
263260

264-
var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader);
265-
266261
var isInterface = (typeDef.Attributes & TypeAttributes.Interface) != 0;
267262
var isAbstract = (typeDef.Attributes & TypeAttributes.Abstract) != 0;
268263
var isGenericDefinition = typeDef.GetGenericParameters ().Count > 0;
@@ -418,6 +413,12 @@ static bool HasJniAddNativeMethodRegistrationAttribute (TypeDefinition typeDef,
418413
return false;
419414
}
420415

416+
static bool IsBuiltInJniAddNativeMethodRegistrationType (string fullName, AssemblyIndex index)
417+
{
418+
return string.Equals (index.AssemblyName, "Java.Interop", StringComparison.Ordinal) &&
419+
string.Equals (fullName, "Java.Interop.JavaProxyObject", StringComparison.Ordinal);
420+
}
421+
421422
/// <summary>
422423
/// For each virtual override method on <paramref name="typeDef"/> that wasn't already
423424
/// collected (no direct [Register]), walks up the base type hierarchy to find a

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,14 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value)
310310
}
311311
}
312312

313-
class AndroidTypeManager : JniRuntime.JniTypeManager {
313+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
314+
class AndroidTypeManager : JniRuntime.ReflectionJniTypeManager {
314315
bool jniAddNativeMethodRegistrationAttributePresent;
315316

316317
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
317318
const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
318319
const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
320+
const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;
319321

320322
public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent)
321323
{
@@ -332,13 +334,25 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
332334
yield return t;
333335
}
334336

337+
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Temporary suppression until legacy typemap entries carry DAM annotations.")]
338+
[return: DynamicallyAccessedMembers (MethodsConstructors)]
339+
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
340+
{
341+
var type = base.GetTypeForSimpleReference (jniSimpleReference);
342+
if (type != null) {
343+
return type;
344+
}
345+
346+
return Java.Interop.TypeManager.GetJavaToManagedType (jniSimpleReference);
347+
}
348+
335349
protected override string? GetSimpleReference (Type type)
336350
{
337351
string? j = JNIEnv.TypemapManagedToJava (type);
338352
if (j != null) {
339353
return GetReplacementTypeCore (j) ?? j;
340354
}
341-
return null;
355+
return base.GetSimpleReference (type);
342356
}
343357

344358
protected override IEnumerable<string> GetSimpleReferences (Type type)
@@ -347,9 +361,12 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
347361
j = GetReplacementTypeCore (j) ?? j;
348362

349363
if (j != null) {
350-
return new[]{j};
364+
yield return j;
365+
yield break;
366+
}
367+
foreach (var r in base.GetSimpleReferences (type)) {
368+
yield return r;
351369
}
352-
return Array.Empty<string> ();
353370
}
354371

355372
protected override IReadOnlyList<string>? GetStaticMethodFallbackTypesCore (string jniSimpleReference)
@@ -623,7 +640,8 @@ static void SplitMethodLine (
623640
}
624641
}
625642

626-
class AndroidValueManager : JniRuntime.JniValueManager {
643+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
644+
class AndroidValueManager : JniRuntime.ReflectionJniValueManager {
627645

628646
Dictionary<IntPtr, IdentityHashTargets> instances = new Dictionary<IntPtr, IdentityHashTargets> ();
629647

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Globalization;
4+
using System.Reflection;
5+
using System.Runtime.CompilerServices;
6+
using Java.Interop;
7+
8+
namespace Microsoft.Android.Runtime;
9+
10+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
11+
abstract class AndroidReflectionJniValueManager : JniRuntime.ReflectionJniValueManager
12+
{
13+
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
14+
15+
public override IJavaPeerable? CreatePeer (
16+
ref JniObjectReference reference,
17+
JniObjectReferenceOptions transfer,
18+
[DynamicallyAccessedMembers (Constructors)]
19+
Type? targetType)
20+
{
21+
EnsureNotDisposed ();
22+
23+
if (!reference.IsValid) {
24+
return null;
25+
}
26+
27+
targetType = targetType ?? typeof (global::Java.Interop.JavaObject);
28+
targetType = GetPeerType (targetType);
29+
30+
if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) {
31+
throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType));
32+
}
33+
34+
var targetSig = Runtime.TypeManager.GetTypeSignature (targetType);
35+
if (!targetSig.IsValid || targetSig.SimpleReference == null) {
36+
throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType));
37+
}
38+
39+
var refClass = JniEnvironment.Types.GetObjectClass (reference);
40+
JniObjectReference targetClass;
41+
try {
42+
targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference);
43+
} catch (Exception e) {
44+
JniObjectReference.Dispose (ref refClass);
45+
throw new ArgumentException ($"Could not find Java class `{targetSig.SimpleReference}`.",
46+
nameof (targetType),
47+
e);
48+
}
49+
50+
if (!JniEnvironment.Types.IsAssignableFrom (refClass, targetClass)) {
51+
JniObjectReference.Dispose (ref refClass);
52+
JniObjectReference.Dispose (ref targetClass);
53+
return null;
54+
}
55+
56+
JniObjectReference.Dispose (ref targetClass);
57+
58+
var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer);
59+
if (peer == null) {
60+
throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture, "Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.",
61+
JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType));
62+
}
63+
peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
64+
return peer;
65+
}
66+
67+
[return: DynamicallyAccessedMembers (Constructors)]
68+
static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
69+
{
70+
if (type == typeof (object)) {
71+
return typeof (global::Java.Interop.JavaObject);
72+
}
73+
if (type == typeof (IJavaPeerable)) {
74+
return typeof (global::Java.Interop.JavaObject);
75+
}
76+
if (type == typeof (Exception)) {
77+
return typeof (JavaException);
78+
}
79+
return type;
80+
}
81+
82+
IJavaPeerable? CreatePeerInstance (
83+
ref JniObjectReference klass,
84+
[DynamicallyAccessedMembers (Constructors)]
85+
Type targetType,
86+
ref JniObjectReference reference,
87+
JniObjectReferenceOptions transfer)
88+
{
89+
var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass);
90+
91+
while (jniTypeName != null) {
92+
JniTypeSignature sig;
93+
if (!JniTypeSignature.TryParse (jniTypeName, out sig)) {
94+
return null;
95+
}
96+
97+
Type? type = GetTypeAssignableTo (sig, targetType);
98+
if (type != null) {
99+
var peer = TryCreatePeerInstance (ref reference, transfer, type);
100+
101+
if (peer != null) {
102+
JniObjectReference.Dispose (ref klass);
103+
return peer;
104+
}
105+
}
106+
107+
var super = JniEnvironment.Types.GetSuperclass (klass);
108+
jniTypeName = super.IsValid
109+
? JniEnvironment.Types.GetJniTypeNameFromClass (super)
110+
: null;
111+
112+
JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose);
113+
klass = super;
114+
}
115+
JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose);
116+
117+
return TryCreatePeerInstance (ref reference, transfer, targetType);
118+
119+
[return: DynamicallyAccessedMembers (Constructors)]
120+
Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType)
121+
{
122+
foreach (var t in Runtime.TypeManager.GetReflectionConstructibleTypes (sig)) {
123+
if (targetType.IsAssignableFrom (t.Type)) {
124+
return t.Type;
125+
}
126+
}
127+
return null;
128+
}
129+
}
130+
131+
IJavaPeerable? TryCreatePeerInstance (
132+
ref JniObjectReference reference,
133+
JniObjectReferenceOptions options,
134+
[DynamicallyAccessedMembers (Constructors)]
135+
Type type)
136+
{
137+
type = Runtime.TypeManager.GetInvokerType (type) ?? type;
138+
139+
var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type);
140+
self.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable);
141+
142+
var constructed = false;
143+
try {
144+
constructed = TryConstructPeer (self, ref reference, options, type);
145+
} finally {
146+
if (!constructed) {
147+
GC.SuppressFinalize (self);
148+
self = null;
149+
}
150+
}
151+
return self;
152+
}
153+
154+
static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType ();
155+
static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) };
156+
157+
protected virtual bool TryConstructPeer (
158+
IJavaPeerable self,
159+
ref JniObjectReference reference,
160+
JniObjectReferenceOptions options,
161+
[DynamicallyAccessedMembers (Constructors)]
162+
Type type)
163+
{
164+
var c = type.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, JIConstructorSignature, null);
165+
if (c != null) {
166+
var args = new object[] {
167+
reference,
168+
options,
169+
};
170+
c.Invoke (self, args);
171+
reference = (JniObjectReference) args [0];
172+
return true;
173+
}
174+
return false;
175+
}
176+
}

src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
namespace Microsoft.Android.Runtime;
2020

21-
class JavaMarshalValueManager : JniRuntime.JniValueManager
21+
class JavaMarshalValueManager : AndroidReflectionJniValueManager
2222
{
2323
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2424

src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
namespace Microsoft.Android.Runtime;
99

10-
class ManagedTypeManager : JniRuntime.JniTypeManager {
10+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
11+
class ManagedTypeManager : JniRuntime.ReflectionJniTypeManager {
1112

1213
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1314
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
1415
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
16+
internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;
1517

1618
public ManagedTypeManager ()
1719
{
@@ -141,6 +143,18 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
141143
}
142144
}
143145

146+
[UnconditionalSuppressMessage ("Trimming", "IL2068", Justification = "Temporary suppression until ManagedTypeMapping type entries carry DAM annotations.")]
147+
[return: DynamicallyAccessedMembers (MethodsConstructors)]
148+
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
149+
{
150+
var type = base.GetTypeForSimpleReference (jniSimpleReference);
151+
if (type != null) {
152+
return type;
153+
}
154+
155+
return ManagedTypeMapping.TryGetType (jniSimpleReference, out var target) ? target : null;
156+
}
157+
144158
protected override IEnumerable<string> GetSimpleReferences (Type type)
145159
{
146160
foreach (var r in base.GetSimpleReferences (type)) {

src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
namespace Microsoft.Android.Runtime;
1818

19-
class SimpleValueManager : JniRuntime.JniValueManager
19+
class SimpleValueManager : AndroidReflectionJniValueManager
2020
{
2121
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2222

src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ namespace Microsoft.Android.Runtime;
1414
/// Type manager for the trimmable typemap path. Delegates type lookups
1515
/// to <see cref="TrimmableTypeMap"/>.
1616
/// </summary>
17-
class TrimmableTypeMapTypeManager : JniRuntime.JniTypeManager
17+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
18+
class TrimmableTypeMapTypeManager : JniRuntime.ReflectionJniTypeManager
1819
{
1920
const string NoSimpleReference = "\0";
21+
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
22+
const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
23+
const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
24+
const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;
2025
readonly ConcurrentDictionary<Type, string> _simpleReferenceCache = new ();
2126

2227
protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
@@ -32,6 +37,21 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
3237
}
3338
}
3439

40+
[UnconditionalSuppressMessage ("Trimming", "IL2063", Justification = "Temporary suppression until trimmable typemap type entries carry DAM annotations.")]
41+
[return: DynamicallyAccessedMembers (MethodsConstructors)]
42+
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
43+
{
44+
var type = base.GetTypeForSimpleReference (jniSimpleReference);
45+
if (type != null) {
46+
return type;
47+
}
48+
49+
if (TrimmableTypeMap.Instance.TryGetTargetTypes (jniSimpleReference, out var types)) {
50+
return types [0];
51+
}
52+
return null;
53+
}
54+
3555
protected override string? GetSimpleReference (Type type)
3656
{
3757
var simpleReference = _simpleReferenceCache.GetOrAdd (type, GetSimpleReferenceUncached);

0 commit comments

Comments
 (0)