Repository navigation
[Java.Interop] Retire experimental JavaInterop1 support - #13030
Conversation
68ad3fd to
c7e07fc
Compare
After removing the unique Java.Interop proxy peers, the remaining object and throwable aliases merge into the Mono.Android Release typemap. No separate Java.Interop typemap assembly is generated for these apps. Require its absence from the CoreCLR APK and NativeAOT ILC inputs while retaining expected Mono.Android/app typemaps and no-framework-export assertions. Leave Debug per-assembly typemap expectations unchanged. Rebuild host tests and run both corrected cases plus Debug-with-trimming packaging: four passed, one existing unsupported configuration skipped. Context: #13030 Co-authored-by: Copilot App <[email protected]>
Remove JavaInterop1 binding and JCW generation after NativeAOT moved to XAJavaInterop1 and Android moved to the trimmable typemap. Retire ManagedPeer, GCUserPeerable, the old proxy classes, and java-interop.jar. Use Android.Runtime.JavaProxyThrowable for both Android runtimes. Preserve managed exception identity, typed peer lookup, and transferred JNI reference ownership. Retain the XA generator scenarios and port array mutation coverage to generated Android bindings. Validated with rebuilt Debug/Release SDKs and CoreCLR/NativeAOT device suites, including 17 regression cases for typed lookup and ownership. Context: #11727 Context: #12133 Context: #13018 Co-authored-by: Copilot App <[email protected]>
Mark JniAddNativeMethodRegistrationAttribute and its CreationOptions property obsolete with warning-only messages explaining that they have no effect. Retain the shipped declarations and property behavior for compatibility, and remove the unread internal copy and Android setter. Document generated Android native registration as the supported path. Debug/Release Java.Interop builds and consumer warning/metadata checks pass. Rebuild the Debug Android SDK and run the CoreCLR device suite: 897 passed, 7 existing skips, no failures. Co-authored-by: Copilot App <[email protected]>
Delete CreationOptions.JniAddNativeMethodRegistrationAttributePresent instead of retaining an obsolete no-op property, as requested. Record its accessor removals in the unshipped API changes and update the docs. The registration attribute remains warning-only obsolete. Debug/Release builds pass. Metadata checks confirm the property is gone and the attribute remains available with its Obsolete annotation. Co-authored-by: Copilot App <[email protected]>
Remove obsolete R8 roots for ManagedPeer, GCUserPeerable, and the two handwritten proxies from both retained-typemap configurations. Update existing R8 tests to reject these roots in both modes. Remove the dead registration-attribute scanner pass/checker and unused XA4251 logging/resource plumbing. Delete the unreferenced embedded AOT profile fixture that still contains ManagedPeer records. Leave generated localization files and the active Android bootstrap roots unchanged. The rebuilt SDK passes. Scanner/typemap regressions pass 73 cases with 4 existing skips. R8 and CoreCLR/NativeAOT packaging tests pass 36 cases with no skips. Metadata inspection confirms the profile is not embedded. Co-authored-by: Copilot App <[email protected]>
After removing the unique Java.Interop proxy peers, the remaining object and throwable aliases merge into the Mono.Android Release typemap. No separate Java.Interop typemap assembly is generated for these apps. Require its absence from the CoreCLR APK and NativeAOT ILC inputs while retaining expected Mono.Android/app typemaps and no-framework-export assertions. Leave Debug per-assembly typemap expectations unchanged. Rebuild host tests and run both corrected cases plus Debug-with-trimming packaging: four passed, one existing unsupported configuration skipped. Context: #13030 Co-authored-by: Copilot App <[email protected]>
78313d8 to
656e253
Compare
|
/review |
|
✅ Android PR Reviewer completed successfully!
|
There was a problem hiding this comment.
✅ LGTM — 0 blocking issues, 0 warnings, 1 suggestion.
The retirement is comprehensive across generator/codegen, runtime/native plumbing, packaging, API compatibility records, documentation, and tests. The shared Android exception path preserves JNI reference ownership and managed-exception identity, while the trimmable value-manager changes retain the Android-specific proxy behavior removed from the base Java.Interop layer. Azure DevOps build #1628115 and all 42 reported checks passed.
Generated by Android PR Reviewer for #13030 · copilot · gpt56 · 256.7 AIC · ⌖ 11.1 AIC · ⊞ 26.4K
Comment /review to run again
| <_RuntimeJar_InTree>$([System.IO.Path]::GetFullPath ('$(XAInstallPrefix)'))\xbuild\Xamarin\Android\java_runtime.jar</_RuntimeJar_InTree> | ||
| <_RuntimeJar_System Condition=" '$(_XamarinAndroidMSBuildDirectory)' != '' ">$(_XamarinAndroidMSBuildDirectory)\java_runtime.jar</_RuntimeJar_System> | ||
| <_JavaRuntimeJarPath Condition=" Exists($(_RuntimeJar_InTree)) ">$(_RuntimeJar_InTree)</_JavaRuntimeJarPath> | ||
| <_JavaRuntimeJarPath Condition=" '$(_JavaRuntimeJarPath)' == '' ">$(_RuntimeJar_System)</_JavaRuntimeJarPath> |
There was a problem hiding this comment.
🤖 💡 MSBuild targets — Consider gating this fallback with Exists($(_RuntimeJar_System)) (or changing the error below to also check !Exists('$(_JavaRuntimeJarPath)')). When _XamarinAndroidMSBuildDirectory is set but the runtime JAR is missing, this assignment makes the property nonempty, so the intended diagnostic is skipped and javac later reports misleading missing-package errors instead.
Rule: Validate file paths before use
|
@dalexsoto review |
There was a problem hiding this comment.
🔵 Needs a closer look
The cross-runtime, native, generator, and public API cleanup is extensive, and the latest CI run remains in progress.
1 open finding
What changed in this PR
Retires experimental JavaInterop1 codegen/runtime infrastructure while preserving XAJavaInterop1 and trimmable Android pipelines.
Changes:
- Removes legacy generators, proxies, JARs, baselines, and GC plumbing.
- Unifies CoreCLR/NativeAOT exception transport.
- Adds validation and regression coverage.
| File area | Description |
|---|---|
external/Java.Interop/** |
Removes JavaInterop1 APIs, codegen, runtime helpers, and baselines. |
src/Mono.Android/** |
Adds shared Android exception handling and GC cleanup. |
src/native/** |
Removes obsolete initialization fields. |
src/java-runtime/** |
Removes retired proxy classes and packaging. |
src/*Build.Tasks/** |
Adds validation and updates R8/packaging behavior. |
tests/** |
Migrates fixtures and expands exception, ownership, GC, and array coverage. |
Documentation/** |
Documents supported targets and exception behavior. |
build-tools/** |
Removes obsolete JAR installation/build wiring. |
🧠 Review effort: Balanced
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
dalexsoto
left a comment
There was a problem hiding this comment.
The retirement is consistent across supported XA/trimmable generation, CoreCLR/NativeAOT exception and peer transport, and SDK packaging. The supported integration scenarios remain covered, and the runtime-JAR fallback now surfaces a missing SDK JAR correctly. The complete current-change and end-to-end review leaves no blocking issue.
Small, independently mergeable .NET 12 cleanup follow-up to [#13030](#13030). Remove only internal `JniRuntime.JniValueManager.GetRuntimeType()` and `JniPeerMembers.JniInstanceMethods.AllocObject()` / `NewObject()` (two files, 26 deleted lines). Semantic references contain only the declarations. Source, generators/templates, reflection strings, conditional sources, friend-assembly tests, and API records were checked for consumers. Public JNI allocation APIs, `StartCreateInstance()` / `FinishCreateInstance()`, constructor caches, `ActivatePeer`, expression marshalling, type management, and ownership logic are unchanged. No framework/package/API-gate upgrade is included.
Follow-up for .NET 12 to [#13030](#13030). With `XAJavaInterop1` now the only supported backend, the binding generator no longer needs to carry a target through its symbol table or indirect generator construction. Remove internal target storage/propagation, construct the generator directly, and remove its unused inheritance hooks. Keep the CLI selector, retired-target rejection, options validation, and public target-taking constructors; the compatibility constructors now forward to target-free overloads and are marked obsolete. Collapse single-target test scaffolding without adding or removing existing test cases. Nullable and non-nullable baselines remain distinct, and all 192 generated files captured from the original suite remain byte-for-byte identical. Changes are confined to the binding generator and its tests. **Local validation** - `dotnet build external/Java.Interop/tools/generator/generator.csproj -v minimal` — passed, 0 warnings/errors. - `dotnet test external/Java.Interop/tests/generator-Tests/generator-Tests.csproj -v minimal` — passed, 478/478, 0 skipped; same count as the pre-change baseline. - Generated-output SHA-256 comparison — passed for all 192 files, including NRT baselines. Direct CLI checks also confirmed identical default/explicit XA output and rejection of `JavaInterop1`. - `make prepare && make all` — preparation passed; the first build hit a bootstrap evaluation failure where compilation lacked generated `ANDROID_*` constants. Re-running unchanged `make all` compiled successfully, then reached the known missing API 37.1 reference-pack bootstrap requirement. - `./dotnet-local.sh build Microsoft.Android.slnx -t:BuildExtraApiLevels -c Debug -v quiet` — passed. - `./dotnet-local.sh build build-tools/create-packs/Microsoft.Android.Sdk.proj -t:ConfigureLocalWorkload -c Debug -v quiet` — passed. - Final `make all` — passed against this branch's own rebuilt SDK, without source/build-property workarounds. - `./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~BindingBuildTest&Name~DotNetBuildBinding|FullyQualifiedName~BindingBuildTest&Name~BindByteArrayInMethodParameter|FullyQualifiedName~BindingBuildTest&Name~NullableReferenceTypes|FullyQualifiedName~BindingBuildTest&Name~BindDefaultInterfaceMethods|FullyQualifiedName~BindingBuildTest&Name~BindingLibraryIncremental'` — passed, 10/10, 0 skipped; CoreCLR and NativeAOT binding coverage. ---- Pull Request [title](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-summary) and [description](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-body) should follow the [`commit-messages.md` workflow documentation](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md), and in particular should include: - [x] Useful description of *why the change is necessary*. - [ ] Links to issues fixed - [x] Unit tests
…13055) Follow-up to [#13030](#13030). `Xamarin.Android.Tasks.AssemblyModifierPipeline` still parses a codegen target and copies it into `StepContext.CodeGenerationTarget`, but no assembly-rewriting step reads that value after JavaInterop1 retirement. Remove the unused field, context property, parser, and task-argument propagation. Also remove `$(_AndroidJcwCodegenTarget)` from the build properties cache, since it no longer affects assembly rewriting. Keep the `CodeGenerationTarget` task input as a no-op so explicit external MSBuild task invocations remain compatible. Keep `JavaPeerStyle`, codegen defaults, and the XA4232/XA4240 retired-value diagnostics unchanged. Assembly-copy/rewrite logic and all other cache entries are untouched. Only four production files change, with 19 deletions and no additions. No tests are added or modified, as requested. ### Local validation All builds and tests use this branch's locally built SDK. | Command | Result | | --- | --- | | `make prepare && make all` | Preparation and solution compilation passed. Initial workload configuration failed because the extra API 37.1 reference assembly had not been built; resolved by the existing targets below. | | `./dotnet-local.sh build Microsoft.Android.slnx -t:BuildExtraApiLevels -c Debug -v quiet` | Passed: 0 errors. | | `./dotnet-local.sh build build-tools/create-packs/Microsoft.Android.Sdk.proj -t:ConfigureLocalWorkload -c Debug -v quiet` | Passed: 0 warnings, 0 errors. | | `./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug --no-restore -v quiet` | Passed: 38 existing warnings, 0 errors. | | `./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~LinkerTests\|Name~UnsupportedCodegenTargetIsRejected\|Name~UnsupportedJcwCodegenTargetIsRejected'` | Passed: 43 passed, 4 skipped, 0 failed. | | `git diff --check` | Passed. | The four skipped cases are existing intentional `Assert.Ignore` cases, not missing prerequisites: - `LinkDescription(False,CoreCLR)`: CoreCLR does not support builds without assembly stores. - `RemoveDesigner(False,CoreCLR)`: CoreCLR supports only assembly stores. - `WarnAboutAppDomains((True, CoreCLR))`: Release-case trimmer-warning issue [dotnet/linker#2982](dotnet/linker#2982). - `WarnAboutAppDomains((True, NativeAOT))`: The same existing Release-case trimmer-warning issue. ---- Pull Request [title](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-summary) and [description](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-body) should follow the [`commit-messages.md` workflow documentation](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md), and in particular should include: - [x] Useful description of *why the change is necessary*. - [ ] Links to issues fixed - [x] Unit tests
## Summary Follow up on [#13030](#13030) by retiring the remaining obsolete expression-marshaling and activation contracts. **This is an intentional .NET 12 source/binary API break, without compatibility stubs.** Supported Android interop uses generated callbacks and trimmable typemap activation; source searches found no consumer of the removed expression pipeline or `ActivatePeer`. Remove: - `JniRuntime.JniMarshalMemberBuilder`, its members, the runtime `MarshalMemberBuilder` property, and `CreationOptions.MarshalMemberBuilder` / `UseMarshalMemberBuilder`. - `Java.Interop.Expressions.JniValueMarshalerContext`, the three expression-generation hooks on `JniValueMarshaler`, and their expression-only helpers and primitive/string/array overrides. - The expression-only `ColorValueMarshaler` and internal `IJavaObjectValueMarshaler`, including their referencing attributes. - The unused `JniValueManager.ActivatePeer` contract and its throwing Android/sample overrides. Retain non-expression `JniValueMarshaler` / argument-state conversion, primitive/string/array functionality, `CreateLocalObjectReferenceArgument`, peer/reference ownership, generated callbacks, and trimmable activation. Keep `GetRuntimeType`, `JniInstanceMethods` allocation helpers, and unrelated public JNI/type-signature APIs unchanged. Update both T4 inputs and checked-in generated files. Remove stale `DynamicallyAccessedMembers` requirements from all eight primitive-array `CreateMarshaledValue` methods: their remaining conversion path constructs a known wrapper and uses type equality/assignability, not reflective activation of the requested target type. Record contract removals through `PublicAPI.Unshipped` entries and the existing Mono.Android ApiCompat baseline mechanism. Replace the obsolete architecture proposal with current behavior and add migration notes. **No new tests are included, per request.** ## Validation All SDK/runtime validation uses this branch's own Release build and repository-pinned SDK `12.0.100-alpha.1.26480.102`, not sibling outputs. Framework paths/parameters below reflect the current checkout's build configuration; this change is intended for .NET 12. | Command | Result | | --- | --- | | `make prepare CONFIGURATION=Release` | Passed. | | `./dotnet-local.sh build build-tools/scripts/Prepare.proj -t:BuildExtraApiLevels -c Release -v minimal` | Passed; built API 37.1/37.2 and ran their API gates. | | `./dotnet-local.sh build build-tools/create-packs/Microsoft.Android.Sdk.proj -t:ConfigureLocalWorkload -c Release -v minimal` | Passed. | | `make all CONFIGURATION=Release` | Passed after completing additional-API bootstrap; initial workload setup lacked the 37.1 reference assembly. | | `dotnet build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -c Release -v minimal -p:DotNetTargetFrameworkVersion=11.0` | Passed, including public API analyzer: zero warnings/errors. | | `./dotnet-local.sh build src/Mono.Android/Mono.Android.csproj -c Release -v minimal -p:DisableApiCompatibilityCheck=False -m:1` | Passed, including stable reference ApiCompat; existing compiler/API-baseline warnings remain. | | `dotnet test external/Java.Interop/tests/generator-Tests/generator-Tests.csproj -c Release -v minimal` | 478 passed, zero failures/skips. | | `dotnet test external/Java.Interop/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj -c Release -v minimal` | 50 passed, zero failures/skips. | Host integration command: ```sh ./dotnet-local.sh test bin/TestRelease/net10.0/Xamarin.Android.Build.Tests.dll \ --filter 'FullyQualifiedName~Build_WithTrimmableTypeMap_Succeeds|FullyQualifiedName~Build_TrimmableTypeMap_WithCollectionExport_Succeeds|FullyQualifiedName~Build_TrimmableTypeMap_UsesMonoAndroidImplementationMetadata|FullyQualifiedName~BindingCallbackFormatSupportsDefaultConsumer' \ --logger 'trx;LogFileName=expression-retirement-host.trx' -v minimal ``` **Passed: 19/19**, covering CoreCLR/NativeAOT typemap builds, generated callbacks, collection exports, and implementation metadata. Device install commands, on exclusively leased `emulator-5554` (arm64, API 36): ```sh ANDROID_SERIAL=emulator-5554 ./dotnet-local.sh build -t:Install \ tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \ -c Release -p:PublishAot=false -p:RuntimeIdentifier=android-arm64 \ '-p:AdbTarget=-s emulator-5554' -m:1 -v minimal ANDROID_SERIAL=emulator-5554 ./dotnet-local.sh build -t:Install \ tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \ -c Release -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \ '-p:AdbTarget=-s emulator-5554' -m:1 -v minimal ``` Both installs passed. Each install was followed by its matching command below, from `tests/Mono.Android-Tests/Mono.Android-Tests`: ```sh ANDROID_SERIAL=emulator-5554 ../../../dotnet-local.sh test Mono.Android.NET-Tests.csproj \ --no-build -c Release -p:PublishAot=false -p:RuntimeIdentifier=android-arm64 \ '-p:AdbTarget=-s emulator-5554' --report-trx \ --results-directory ../../../bin/TestRelease/TestResults/expression-retirement-coreclr ANDROID_SERIAL=emulator-5554 ../../../dotnet-local.sh test Mono.Android.NET-Tests.csproj \ --no-build -c Release -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \ '-p:AdbTarget=-s emulator-5554' --report-trx \ --results-directory ../../../bin/TestRelease/TestResults/expression-retirement-nativeaot ``` **CoreCLR: 924 passed, 7 existing skips, zero failures. NativeAOT: 869 passed, 7 existing skips, zero failures.** TRX results confirm JNI/primitive and object arrays, constructors, callbacks/exports, exceptions, peer control blocks, and reference ownership, including all 38 constructor activation tests and 58 transferred-reference tests on both runtimes. Only the test package was uninstalled afterward. Both T4 templates regenerate the checked-in files exactly; `git diff --check` passes. ### Review follow-up The runtime/SDK test results above were collected for commit `1251576d3d98f093ca1879314e12f7c97771091f`. Follow-up commit `ce74deda0408d0b8268adc8437d07f66a5d6627c` only removes the stale primitive-array trimming annotations and their unused constant. `./dotnet-local.sh build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -c Release -v minimal -p:DotNetTargetFrameworkVersion=11.0 -p:EnableTrimAnalyzer=true -m:1` **passed with zero warnings/errors**, including API and trimming analyzers. Regeneration matches exactly. IL comparison confirmed all 1,333 emitted method bodies remain identical after normalizing RVA addresses and compiler-generated iterator numbering; all eight public conversion APIs remain and their stale parameter attributes are absent. Full SDK/device suites were not rerun for this metadata-only follow-up. ### Standalone NativeAOT sample `./dotnet-local.sh build external/Java.Interop/samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj -c Release -r osx-arm64 -v minimal -p:DotNetTargetFrameworkVersion=11.0 -p:RestoreConfigFile="$PWD/NuGet.Config" -m:1` passes. Running the compiled NativeAOT library from Java passes and returns `Hello from .NET NativeAOT!`. Optional `publish` remains **failed with MSB3030 in the unchanged `_NativeLibRequiresLibPrefix` target**: it expects `Hello-NativeAOTFromJNI.dylib`, but the pinned SDK emits `libHello-NativeAOTFromJNI.dylib`. This is not claimed to be baseline-proven; no unrelated sample naming-target fix is included. The sample build and actual JNI execution validate the changed override removal. ---- Pull Request [title](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-summary) and [description](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-body) should follow the [`commit-messages.md` workflow documentation](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md), and in particular should include: - [x] Useful description of *why the change is necessary*. - [ ] Links to issues fixed - [x] Unit tests No issue is closed. Existing unit/integration/runtime coverage was run; no new tests were added.
) Follow up to [#13030](#13030). JavaInterop1 retirement leaves `java-interop-mono.cc` without any code and `java-interop-mono.h` with only Mono header imports. Native validation still compiles that empty adapter and downloads a host Mono runtime pack pinned to 8.0.13 solely for those unused headers. Remove the empty adapter/header, CMake source/include wiring, Mono path properties and arguments, and runtime-pack download. Also remove the unused Mono configuration overrides, dead `MonoInfo.props` import, and orphaned `mono.mk` file. No tracked reference, dynamic/wildcard include, or documented entry point consumes that Make fragment. Keep the native validation project and compilation of the shared dlfcn, utility, and core sources. Archive paths, native source/header inputs, incremental behavior, and clean behavior remain unchanged. Generic native toolchain scripts and Android runtime/native-host CMake are untouched. The third-party-notice audit requires no changes: the removed runtime pack is Microsoft-owned and build-only, and the Java.Interop notice remains. ### Local validation All commands below passed on macOS arm64; builds reported zero warnings and errors. For the isolated builds, `NUGET_PACKAGES` and `NUGET_HTTP_CACHE_PATH` pointed to fresh, private cache directories without removing or modifying shared/global packages. | Command | Result | | --- | --- | | `dotnet restore external/Java.Interop/src/java-interop/java-interop.csproj -p:BaseIntermediateOutputPath=obj/no-mono/ -v:minimal` | Passed; isolated cache contains only NoTargets and .NET analyzers, with no Mono runtime pack. | | `dotnet build external/Java.Interop/src/java-interop/java-interop.csproj --no-restore -p:BaseIntermediateOutputPath=obj/no-mono/ -v:minimal` | Passed; Debug arm64 archive contains exactly the three active source objects. | | `dotnet build external/Java.Interop/src/java-interop/java-interop.csproj -c Release -p:BaseIntermediateOutputPath=obj/no-mono/ -v:minimal` | Passed; Release arm64 archive. | | `dotnet build external/Java.Interop/src/java-interop/java-interop.csproj -p:BaseIntermediateOutputPath=obj/no-mono-x64/ -p:NETCoreSdkRuntimeIdentifier=osx-x64 -v:minimal` | Passed; x86_64 archive verified with `lipo`. | | `dotnet clean external/Java.Interop/src/java-interop/java-interop.csproj -v:minimal` | Passed; default validation archive removed. | | `dotnet build external/Java.Interop/src/java-interop/java-interop.csproj --no-restore -v:minimal` | Passed after clean and on the unchanged subsequent build; also passed after deleting `mono.mk`. | Additional local checks passed: all three source files, four headers, CMakeLists.txt, the project, and its targets invalidate `_BuildLibs` when touched; unchanged builds skip that target and preserve the archive mtime; clean and missing-output builds recreate the archive. Fresh CMake/Ninja configuration, output-directory override, incremental build, and clean/rebuild also passed. Restore graphs, active compiler header dependencies, and compile commands contain no Mono dependency. Ad hoc native smoke checks linked against the arm64 and x64 archives and passed strdup/free and dynamic-library load/symbol/close success and error cases; x64 execution used Rosetta. Edited XML and `git diff --check` passed. No new unit-test infrastructure is added for this unused-code removal. Windows/MSVC and Linux hosts were unavailable locally and remain untested. No full Android SDK/device build was run: active runtime sources and Android CMake behavior are unchanged. ---- Pull Request [title](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-summary) and [description](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md#commit-body) should follow the [`commit-messages.md` workflow documentation](https://github.com/dotnet/android/blob/main/Documentation/workflow/commit-messages.md), and in particular should include: - [x] Useful description of *why the change is necessary*. - [ ] Links to issues fixed - [ ] Unit tests

Summary
Retire the experimental
JavaInterop1binding/Java Callable Wrapper (JCW) generation paths and their obsolete runtime infrastructure. KeepXAJavaInterop1as the supported binding/legacy JCW shape, and keep the trimmable typemap's Java generator as a separate, targetless pipeline.This implements the Android cleanup tracked by #11727, including removal of
ManagedPeer,GCUserPeerable, the handwritten Java.Interop proxies, andjava-interop.jar. Preserve managed-object transport and consolidate exception transport onto the existing generatedAndroid.Runtime.JavaProxyThrowablefor both CoreCLR and NativeAOT.The branch is rebased onto
maincommit058e71b5867c63fd92a00ae545c7899f2b60ed6e, including the Android test relocation in #13019 and the retained-typemap R8 pipeline in #12829. This remains a draft; local validation is documented below, not presented as a claim that CI is green. No tracking issue is auto-closed.Android compatibility: why this can be removed
Normal .NET for Android and .NET MAUI codegen is unchanged
The standard .NET for Android application/binding pipeline, also used by .NET MAUI Android applications, uses
XAJavaInterop1, not the experimentalJavaInterop1shape. This PR preserves that supported code-generation path.NativeAOT previously selected
JavaInterop1for its JCWs. Android NativeAOT has remained an experimental runtime option, documented by XA1040, and merged #12133 already switched it toXAJavaInterop1and unified the Android GC bridge aroundmono.android.IGCUserPeer. Before this PoC, the SDK defaults already selectedXAJavaInterop1for bothAndroidCodegenTargetand_AndroidJcwCodegenTarget.The old
JavaInterop1option was explicitly documented as experimental in the build-property documentation. Its historical NativeAOT-default description is stale after #12133 and is corrected here.The supported Android paths have already migrated:
ManagedPeerfor native registration.All these prerequisites are merged. No supported current Android code-generation or activation path needs
ManagedPeer.constructor JavaInterop1-style wrappers. Some obsolete helpers were still packaged or initialized through common runtime plumbing; removing that leftover machinery does not remove the codegen used by normal .NET for Android or .NET MAUI apps.Each removed runtime component has an Android replacement or no supported consumer
Java.Interop.ManagedPeerandnet.dot.jni.ManagedPeernet.dot.jni.GCUserPeerable,jiAddManagedReference, andjiClearManagedReferencesmono.android.IGCUserPeer. Migrate surviving handwritten Android fixtures to that contract; remove the obsolete runtime field, host/managed marker lookups, and matching managed/native initialization-struct field. Android GC/reference tracking is retained.Java.Interop.JavaProxyObjectandnet.dot.jni.internal.JavaProxyObjectTrimmableJavaProxyObjectand its Java identity semantics; the removed handwritten class is not its implementation.Java.Interop.JavaProxyThrowableandnet.dot.jni.internal.JavaProxyThrowableAndroid.Runtime.JavaProxyThrowable, as CoreCLR already did. No thirdTrimmableJavaProxyThrowableis introduced.java-interop.jarandroid.jarandjava_runtime.jarinstead.SelfRegistration,TestType,TestTypeTests, and the old Java.InteropCallNonvirtualDerived2fixtureThe removed managed runtime implementation types are internal. Public
JavaObject,JavaException,JniPeerMembers, andIJavaPeerableremain. The existing Android throwable and managed-object proxy implementations also remain. The explicit public configuration-property removal below is documented separately.Supported construction and registration remain intact
Ordinary managed-to-Java construction does not need
ManagedPeer:JavaObjectallocates the Java object, associates the managed peer, then invokes the Java constructor throughStartCreateInstance/FinishCreateInstance.For Java-to-managed activation and callbacks, supported Android types use generated Android/trimmable dispatch and registration. The trimmable generator registers natives through
TrimmableTypeMap.RegisterNativeMethods()/mono.android.Runtime.registerNatives(Class), independently of either removedJavaInterop1enum value. No reflective compatibility shim is reintroduced.Implementation changes
Generator and build surface
CodeGenerationTarget.JavaInterop1,JavaPeerStyle.JavaInterop1, their generator/source-writer/JCW branches, thejavainterop1CLI selector, and unused SDK configuration selecting that shape.XAJavaInterop1output and the independent trimmable pipeline. This is not a rename of the typemap generator to anotherAndroidCodegenTargetvalue.AndroidCodegenTargetvalues with actionable XA4232 diagnostics for application and library builds. Retain the existing XA4240 rejection for retired_AndroidJcwCodegenTargetvalues, and test rejection rather than silently falling back.JAVA_INTEROP1/TODO_JAVA_INTEROP1/TryJavaInterop1scaffolding. Move shared XML inputs/support sources fromexpected.jiintoTestInputsso supported tests do not depend on a retired baseline directory.Shared Android exception handling
Add a shared internal
AndroidExceptionManagerand make CoreCLR and NativeAOT delegate wrapping/unwrapping to it.JniEnvironment.Exceptions.Throw(Exception)dispatches to the active runtime'sRaisePendingExceptionimplementation.The Android implementation wraps ordinary managed exceptions in the generated Android proxy, recognizes existing Java exception peers, and returns the original managed exception when its proxy returns through JNI or appears as a Java cause. Typed value-manager lookup distinguishes peer from unboxed exception: requests for
Java.Lang.Throwable,JavaException, orIJavaPeerablereturn the Java peer; requests for compatible managed exception/object types return the original managed value.Shared lookup releases incoming JNI references in an option-aware
finally, on successful and failing extraction paths. Borrowed references remain valid; transferred local/global references are released exactly once. Permanent regressions cover typed lookup and four extraction failure stages across all three ownership modes.The base
JniRuntime.RaisePendingExceptionstill throws an existingJavaException. Android overrides supply managed-exception wrapping; a runtime without an implementation receives an explicitNotSupportedExceptioninstead of silently depending on a deleted proxy. The exception contract is documented.Finish removing associated scaffolding
proguard_typemap.cfgandproguard_typemap_coreclr.cfg, along with their misleading comments. Preserve Android bootstrap/GC roots. Update the existing R8 response tests to assert that neither retained-typemap mode emits retired-helper roots.JniAddNativeMethodRegistrationAttributewith warning-only[Obsolete], explicitly stating that it has no effect. Delete the unused publicJniRuntime.CreationOptions.JniAddNativeMethodRegistrationAttributePresentproperty rather than retaining a no-op setting; record its accessor removals with*REMOVED*entries inPublicAPI.Unshipped.txt. Its unread internal copy and Android assignment are also gone.custom.aotprofileProjectTools fixture containing removedManagedPeer.Init/.cctorrecords. Source searches found no direct or dynamically enumerated profile consumer; the resource wildcard was its remaining inclusion path. Rebuilt-assembly inspection confirms it is no longer embedded. Do not regenerate a profile for retired functionality.No non-English
.resxor generated localization files are modified.Reconciliation with current main
Preserve #13019's local C# compilation and Java fixture glob under
tests/Mono.Android-Tests/Java.Interop-Tests/, including itsAndroidJavaSourceexclusion to prevent duplicate compilation. Apply fixture deletions andIGCUserPeerconversions at the relocated paths; do not restore the external test source directory/globs. Keep the Android-specificGetThisimplementation and comments.Preserve upstream native host startup/logging and assembly-store changes. Preserve #12829's retained-key R8 pipeline; do not reintroduce DGML generation or alter new targets to accommodate the old validation setup.
Android coverage preserved and added
expected.jiintegration scenarios retain XA baselines and active runners. RestoreJava_Lang_Objectagainst XA and refresh its previously unused snapshot. Shared inputs are migrated, not deleted.WriteInterfaceFieldssnapshot already had no test consumer before cleanup.ConstructorActivationTests, generated-bindingBindingTests.VirtualMethodBinding, andExceptionTests.ManagedJavaManaged_FinallyExecutedretain activation, multi-level dispatch, and managed/Java/managed exceptions with Javafinallyexecution.int[],int[][],int[][][], with null/invalid/success results and complete copy-back assertions. These exercise generatedJNIEnv.CopyArray, not handwritten replacement marshalling.Most deletions are retired generated baselines and duplicate scaffolding, not supported Android runtime scenarios.
Local validation
Latest property/scanner/resource/profile cleanup
The following checks ran against the final cleanup sources with a rebuilt Debug Android SDK. These are distinct from the earlier complete post-rebase device runs below.
bin/Debug/dotnet/dotnet build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -c Debug -v quiet -p:JavaCPath=/usr/bin/javac -p:JarPath=/usr/bin/jarand the same command with-c Releasebin/Debug/dotnet/dotnet build src/Mono.Android/Mono.Android.csproj -c Debug -v quietbin/Debug/dotnet/dotnet build build-tools/scripts/Prepare.proj -t:BuildDotNet -c Debug -v quietwith Debug workload rootsbin/Debug/dotnet/dotnet test src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/Microsoft.Android.Build.Tasks.Tests.csproj -c Debug -v quiet --filter 'FullyQualifiedName~TrimmableTypeMap|FullyQualifiedName~TypeMapProguardTargetsTests'bin/Debug/dotnet/dotnet build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug -v quietJava.Interop.dllandXamarin.ProjectTools.dllObsolete, deleted profile absent from manifest resources.git diff --checkThis combined invocation passed 36 cases with 0 skips/failures: all R8 task cases, both retained-rule R8 integration modes, CoreCLR/NativeAOT throwable packaging, and ProGuard builds.
Full post-rebase runtime/device validation
Both configuration-specific SDKs were rebuilt after the rebase using
Prepare.proj -t:BuildDotNet -c Debug/Release. Set matchingDOTNETSDK_WORKLOAD_MANIFEST_ROOTS="$PWD/bin/<configuration>/lib/sdk-manifests"andDOTNETSDK_WORKLOAD_PACK_ROOTS="$PWD/bin/<configuration>/lib", withANDROID_SERIAL=emulator-5554. Explicit SDK paths avoiddotnet-local.shpreferring an existing Release SDK over Debug.Passed: 897 CoreCLR tests, 7 existing skips, 0 failures, including relocated JNI tests and all 17 permanent regressions. This full CoreCLR suite also passed again during the subsequent deprecation step (results under
ObsoleteRegistrationResults), before the latest host-only scanner/root/profile cleanup.Passed: 842 NativeAOT tests, 7 existing skips, 0 failures, including all 17 regressions. The current retained-key R8 pipeline produced 328 retained keys, including the Android throwable;
r8.jsonconfirmed shrinking enabled. No old native-output-path override or DGML workaround was used, and no trimming/R8/AOT was disabled. Build-only-m:1is omitted from the test invocation because MTP rejects the forwarded--moption.These complete device suites were not rerun for the latest scanner/resource/profile-only additions; the latest 36-case host integration run builds and verifies both CoreCLR and NativeAOT packages against the updated resources.
Other post-rebase tooling and generated-XA coverage
bin/Debug/dotnet/dotnet test external/Java.Interop/tests/generator-Tests/generator-Tests.csproj -c Debug -v quiet -p:JavaCPath=/usr/bin/javac -p:JarPath=/usr/bin/jarexternal/Java.Interop/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers-Tests.csprojexternal/Java.Interop/tests/Java.Interop.Tools.Generator-Tests/Java.Interop.Tools.Generator-Tests.csprojbin/Debug/dotnet/dotnet test src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/Microsoft.Android.Build.Tasks.Tests.csproj -c Debug -v quiet --filter FullyQualifiedName~GenerateTrimmableTypeMapTestsbin/Debug/dotnet/dotnet test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~InvalidConfigTests|Name~TrimmableTypeMap_PackagesAndroidJavaProxyThrowable|Name~BuildProguardEnabledProject|Name~RetainedTypeMapRulesDriveR8' -v quietGenerated-XA JCW device commands used
tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csprojwithbuild -t:Install, then matchingtest --no-buildfrom its directory,--device emulator-5554 --report-trx, and configuration-specific workload roots:-c Debug -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=CoreCLR -p:AndroidCodegenTarget=XAJavaInterop1 -p:RuntimeIdentifier=android-arm64 '-p:AdbTarget=-s emulator-5554'— 46 passed, 2 existing explicit skips, including arrays, dispatch, and managed/Java/finally exceptions.-c Release -p:BuildInParallel=false -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=NativeAOT -p:PublishAot=true -p:AndroidCodegenTarget=XAJavaInterop1 -p:RuntimeIdentifier=android-arm64 -p:IncludeCategories=ArrayMutation -p:_AndroidEnableTypemapR8Trimming=true -p:_SkipNdkResolution=false '-p:AdbTarget=-s emulator-5554', plus build-only-m:1— 3 passed, 0 skips/failures, with retained-key R8 trimming enabled.Post-rebase artifact inspection verified all 10 Debug/Release runtime JARs omit the retired helpers,
java-interop.jaris absent, the relocated fixture JAR omits deleted fixtures, and both signed runtime-test APKs contain the Android throwable without the retired helper descriptors.Device coverage is arm64 only, not a complete ABI/device matrix. No CI success is claimed.
Compatibility and follow-ups
ManagedPeer/JavaInterop1 contract must be regenerated/migrated. No compatibility shim is retained. #11635 tracks that question; [Java.Interop] Remove support for experimentalJavaInterop1codegen #11727 documents the removal direction. Normal .NET for Android/MAUI codegen remains intact.JniAddNativeMethodRegistrationAttributePresentconfiguration property is intentionally removed; the attribute itself remains warning-only obsolete. Existing code setting that property must remove the setting, which no longer controlled any Android runtime behavior.Related issues and PRs
Pull Request
title and
description
should follow the
commit-messages.mdworkflow documentation, and in particular should include: