You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Current dotnet/android development sources; CoreCLR and NativeAOT. This is an architectural cleanup proposal, not an OS-specific failure.
Description
Move Java.Interop and Mono.Android toward a single coherent Android interop implementation, eliminating historical "two ways of doing things" where the split is no longer necessary.
Direction: Android's supported runtime requirements should drive this work. Preserving Java.Interop independently of Android is not, by itself, a reason to retain duplicate implementations. Preserve required semantics and public contracts, rather than maintaining parallel internal mechanisms solely because of their historical ownership.
This should be incremental follow-up work, separate from PR #12890. Aligning behavior need not wait for an assembly merge; evaluate assembly/dependency consolidation separately if it meaningfully simplifies the architecture.
First concrete target: exception proxies
CoreCLR dispatch uses Android.Runtime.JavaProxyThrowable, backed by android.runtime.JavaProxyThrowable. NativeAOT creates JreRuntime and inherits Java.Interop's exception dispatch, which uses Java.Interop.JavaProxyThrowable, backed by net.dot.jni.internal.JavaProxyThrowable.
These paths differ in more than the Java name: creation, managed-exception unwrapping, stack-trace translation, and uncaught-exception reporting need to agree. Java.Lang.Throwable.FromException() already creates the Android platform proxy.
Removing the legacy platform JAR in #12890 exposed this split in an R8 packaging test. The immediate test fix correctly checks each runtime's current proxy, but does not resolve the architectural duplication. Both runtime/RID variants pass with that fix, and Java-to-managed-to-Java exception propagation was verified locally for both runtimes.
Converge CoreCLR and NativeAOT on one Android exception-proxy implementation, preferably the existing android.runtime.JavaProxyThrowable.
Unify creation and unwrapping across runtime dispatch, direct Java.Interop exception-throwing APIs, and NativeAOT uncaught-exception reporting. Do not change only the Java class name or one runtime override.
Retain original managed exceptions and their identity where supported, preserve Java exceptions, and define consistent message/stack-trace behavior.
Ensure real NativeAOT reachability retains the generated platform peer and its required constructors under ILC and R8; do not restore the legacy platform JAR.
Once all consumers are migrated, remove the redundant proxy implementation and associated generated-Java, JAR, typemap, native JNI lookup, and keep-rule plumbing that is genuinely unused.
Broader convergence audit
Inventory remaining parallel mechanisms for proxy creation/unboxing, peer lifetime and GC bookkeeping, activation/type resolution, Java generation, and runtime packaging. For each, identify whether the difference is required by CoreCLR/NativeAOT or merely historical, then split actionable consolidation work into focused PRs.
Do not assume apparently similar implementations are interchangeable: for example, managed-object proxy equality/hash-code/string-conversion semantics need an explicit decision before consolidation.
Both runtimes use the same Android proxy for managed exception dispatch. Tests cover original-exception round-trips, exported methods and throwing constructors, nested callbacks, uncaught-exception reporting/events, stack traces, pending-exception cleanup, and peer/reference lifetime. Release builds with and without R8 retain the required peer/constructors; obsolete proxy artifacts are absent after their consumers have been removed. Runtime-specific assertions remain only where there is a documented, necessary behavioral difference.
Steps to Reproduce
This is a convergence proposal rather than a new failure report. The current split can be inspected in:
src/Mono.Android/Android.Runtime/AndroidRuntime.cs and JavaProxyThrowable.cs.
src/Mono.Android/Java.Lang/Throwable.cs.
src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs and UncaughtExceptionMarshaler.cs.
external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.cs, JniEnvironment.Errors.cs, JavaProxyThrowable.cs, and JniRuntime.JniValueManager.cs.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs, test BuildProguardEnabledProject.
Did you find any workaround?
PR #12890's runtime-specific DEX assertions reflect the current supported behavior. They are an immediate test correction, not the proposed convergence. No legacy compatibility path needs to be restored.
Relevant log output
N/A. The investigation, focused fix, exact validation commands, and local results are recorded in this PR comment.
Android framework version
net11.0-android (Preview)
Affected platform version
Current dotnet/android development sources; CoreCLR and NativeAOT. This is an architectural cleanup proposal, not an OS-specific failure.
Description
Move Java.Interop and Mono.Android toward a single coherent Android interop implementation, eliminating historical "two ways of doing things" where the split is no longer necessary.
Direction: Android's supported runtime requirements should drive this work. Preserving Java.Interop independently of Android is not, by itself, a reason to retain duplicate implementations. Preserve required semantics and public contracts, rather than maintaining parallel internal mechanisms solely because of their historical ownership.
This should be incremental follow-up work, separate from PR #12890. Aligning behavior need not wait for an assembly merge; evaluate assembly/dependency consolidation separately if it meaningfully simplifies the architecture.
First concrete target: exception proxies
CoreCLR dispatch uses
Android.Runtime.JavaProxyThrowable, backed byandroid.runtime.JavaProxyThrowable. NativeAOT createsJreRuntimeand inherits Java.Interop's exception dispatch, which usesJava.Interop.JavaProxyThrowable, backed bynet.dot.jni.internal.JavaProxyThrowable.These paths differ in more than the Java name: creation, managed-exception unwrapping, stack-trace translation, and uncaught-exception reporting need to agree.
Java.Lang.Throwable.FromException()already creates the Android platform proxy.Removing the legacy platform JAR in #12890 exposed this split in an R8 packaging test. The immediate test fix correctly checks each runtime's current proxy, but does not resolve the architectural duplication. Both runtime/RID variants pass with that fix, and Java-to-managed-to-Java exception propagation was verified locally for both runtimes.
android.runtime.JavaProxyThrowable.Broader convergence audit
Inventory remaining parallel mechanisms for proxy creation/unboxing, peer lifetime and GC bookkeeping, activation/type resolution, Java generation, and runtime packaging. For each, identify whether the difference is required by CoreCLR/NativeAOT or merely historical, then split actionable consolidation work into focused PRs.
Do not assume apparently similar implementations are interchangeable: for example, managed-object proxy equality/hash-code/string-conversion semantics need an explicit decision before consolidation.
Related: #11727 — Remove support for experimental JavaInterop1 codegen. Coordinate with that work and revisit its standalone-preservation assumptions rather than creating contradictory cleanup plans.
Acceptance criteria for the exception-proxy phase
Both runtimes use the same Android proxy for managed exception dispatch. Tests cover original-exception round-trips, exported methods and throwing constructors, nested callbacks, uncaught-exception reporting/events, stack traces, pending-exception cleanup, and peer/reference lifetime. Release builds with and without R8 retain the required peer/constructors; obsolete proxy artifacts are absent after their consumers have been removed. Runtime-specific assertions remain only where there is a documented, necessary behavioral difference.
Steps to Reproduce
This is a convergence proposal rather than a new failure report. The current split can be inspected in:
src/Mono.Android/Android.Runtime/AndroidRuntime.csandJavaProxyThrowable.cs.src/Mono.Android/Java.Lang/Throwable.cs.src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.csandUncaughtExceptionMarshaler.cs.external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.cs,JniEnvironment.Errors.cs,JavaProxyThrowable.cs, andJniRuntime.JniValueManager.cs.src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs, testBuildProguardEnabledProject.Did you find any workaround?
PR #12890's runtime-specific DEX assertions reflect the current supported behavior. They are an immediate test correction, not the proposed convergence. No legacy compatibility path needs to be restored.
Relevant log output
N/A. The investigation, focused fix, exact validation commands, and local results are recorded in this PR comment.