Repository navigation
[tests] Move core JNI tests into the Android device suite - #13019
Conversation
The Android runtime uses the trimmable typemap managers, and the host-JVM test runs have been removed. Delete the remaining reflection managers, their generated helper tables, and the dormant JVM harness. Keep runtime-disposal coverage with non-reflection test doubles, remove unshipped API declarations and suppressions, and update test guidance. Context: #12894 Co-authored-by: Copilot App <[email protected]>
Remove JniProxyRuntime and its custom JNI manager test doubles rather than preserving a second runtime implementation solely for a disposal test. Delete Dispose_ClearsJniEnvironment, its only consumer, and the unused imports. Retain tests using the active Android runtime. Co-authored-by: Copilot App <[email protected]>
Keep test guidance focused on the supported Android and tooling tests, without mentioning the removal of the standalone host-JVM harness. Co-authored-by: Copilot App <[email protected]>
The shared JavaVMFixture no longer initializes a JVM. Remove its unused capability flags and the SafeHandle-dependent transition test, which is always skipped because JniObjectReference has no gcHandle. Drop the remaining inheritance clauses so Android interop tests use the active runtime directly, without an empty shared fixture. Co-authored-by: Copilot App <[email protected]>
Keep core JNI test sources and Java fixtures beside their Android test project after the standalone JVM harness removal. Use the default C# source glob and a single local fixture JAR, with the Android-safe GetThis implementation for every type map. Retain Android assertions, capability guards, and runtime exclusions. Remove unsupported desktop remapping tests and their unused fixtures. Update the interop documentation and test discovery guidance. Context: #13018 Co-authored-by: Copilot App <[email protected]>
Keep core JNI test sources and Java fixtures beside their Android test project after the standalone JVM harness removal. Use the default C# source glob and a single local fixture JAR, with the Android-safe GetThis implementation for every type map. Retain Android assertions, capability guards, and runtime exclusions. Remove unsupported desktop remapping tests and their unused fixtures. Update the interop documentation and test discovery guidance. Context: #13018 Co-authored-by: Copilot App <[email protected]>
b216e11 to
268d756
Compare
…sival-android-interop-test-migration
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The relocation leaves a project linked to a deleted source path and contains JNI/test-discovery correctness defects.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Relocates core JNI tests and fixtures into the Android device-test project while removing the obsolete desktop harness and duplicate fixture paths.
Changes:
- Moves core C#/Java JNI tests beside their Android test project.
- Consolidates Android fixture selection and simplifies project globs.
- Removes unsupported desktop-only tests and updates related guidance.
| File | Description |
|---|---|
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/TestType.java |
Relocates the TestType fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/SelfRegistration.java |
Relocates self-registration fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/RegisterNativesTestType.java |
Relocates native-registration fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/ObjectHelper.java |
Relocates remapping helper. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/MyJavaInterfaceImpl.java |
Relocates interface implementation. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/JavaInterface.java |
Relocates Java interface fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/GetThis.java |
Consolidates the Android-safe fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/DesugarAndroidInterface$_CC.java |
Relocates desugaring companion fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/CallNonvirtualDerived2.java |
Relocates derived JNI fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/CallNonvirtualDerived.java |
Relocates nonvirtual-call fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/CallNonvirtualBase.java |
Relocates base JNI fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/AndroidInterface.java |
Relocates desugaring interface. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/TestTypeTests.cs |
Moves TestType device tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/TestType.cs |
Moves the TestType binding. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/SelfRegistration.cs |
Moves managed registration fixture. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniValueMarshalerAttributeTests.cs |
Moves marshaler attribute tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniTypeUtf8Test.cs |
Retains Android UTF-8 assertions. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniTypeTest.cs |
Retains Android exception assertions. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniTypeSignatureTest.cs |
Moves signature tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniTypeSignatureAttributeTest.cs |
Moves signature attribute tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs |
Moves type-manager coverage. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs |
Moves active-runtime tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniReferenceLeakMeasurementTests.cs |
Moves leak protocol regressions. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniReferenceLeakMeasurement.cs |
Moves shared leak measurement logic. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs |
Retains Android-supported peer tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniPeerMembersExtensions.cs |
Moves peer invocation helpers. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniNativeInterfaceTests.cs |
Moves but excludes internal-layout tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniMarshalTests.cs |
Moves marshaling tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JniEnvironmentTests.cs |
Moves JNI environment tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaSingleArrayContractTests.cs |
Moves float-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaSByteArrayContractTests.cs |
Moves sbyte-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaPrimitiveArrayContract.cs |
Moves primitive-array base coverage. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaPeerableExtensionsTests.cs |
Moves peer conversion tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs |
Removes obsolete platform guard. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaObjectExtensionsTests.cs |
Moves object extension tests. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs |
Moves object-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaInt64ArrayContractTests.cs |
Moves long-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaInt32ArrayContractTests.cs |
Moves int-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaInt16ArrayContractTests.cs |
Moves short-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaExceptionTests.cs |
Retains Android Throwable handling. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaDoubleArrayContractTests.cs |
Moves double-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaCharArrayContractTests.cs |
Moves char-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaBooleanArrayContractTests.cs |
Moves boolean-array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/JavaArrayContract.cs |
Moves shared array contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/GetThis.cs |
Moves GetThis managed binding. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/FinalizerHelpers.cs |
Moves finalizer test utility. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/CallNonvirtualDerived2.cs |
Moves second-level binding. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/CallNonvirtualDerived.cs |
Moves derived binding. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/CallNonvirtualBase.cs |
Moves base binding. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop/AssemblyInfoTests.cs |
Moves assembly metadata test. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.targets |
Uses local fixtures and incremental copying. |
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj |
Switches to the default source glob. |
tests/Mono.Android-Tests/Java.Interop-Tests/java-trimmable/net/dot/jni/test/GetThis.java |
Removes duplicate GetThis variant. |
tests/Mono.Android-Tests/Java.Interop-Tests/Cadenza/BaseRocksFixture.cs |
Moves shared contract helper. |
tests/Mono.Android-Tests/Java.Interop-Tests/Cadenza.Collections/ListContract.cs |
Moves list contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Cadenza.Collections/EnumerableContract.cs |
Moves enumerable contracts. |
tests/Mono.Android-Tests/Java.Interop-Tests/Cadenza.Collections/CollectionContract.cs |
Moves collection contracts. |
external/Java.Interop/tests/Java.Interop-Tests/java/net/dot/jni/test/FieldRemapRenamedBase.java |
Removes desktop-only remapping fixture. |
external/Java.Interop/tests/Java.Interop-Tests/java/net/dot/jni/test/FieldRemapDerived.java |
Removes desktop-only derived fixture. |
external/Java.Interop/tests/Java.Interop-Tests/java/net/dot/jni/test/FieldRemapBase.java |
Removes desktop-only base fixture. |
external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs |
Removes former host test source. |
external/Java.Interop/tests/Java.Interop-Tests/Java.Interop-Tests.targets |
Removes obsolete desktop JAR targets. |
external/Java.Interop/Documentation/BuildConfiguration.md |
Documents the Android test location. |
.github/skills/tests/SKILL.md |
Updates test routing guidance. |
.github/skills/tests/references/test-catalog.md |
Updates the core JNI catalog entry. |
.github/instructions/java-interop.instructions.md |
Documents Android-only core tests. |
Normalize the fixture JAR path before extracting its filename so the incremental copy target tracks the actual destination on Unix. Remove obsolete host-JVM leak test instructions and keep the supported Android test commands and collection protocol documentation. Co-authored-by: Copilot App <[email protected]>
Link the shared JNI reference leak measurement helper from its Android test directory. The old external path no longer exists and caused CS2001 in both the ordinary and isolated leak JCW test lanes. Co-authored-by: Copilot App <[email protected]>
|
@dalexsoto review |
dalexsoto
left a comment
There was a problem hiding this comment.
The complete current 68-file review, including all 53 rename origins and six deleted origins, separate integration/completeness pass and final independent sweep found no blocker. Supported Android assertions, capability guards, merged cleanup/collection regressions, test discovery/rooting and VM/instrumentation setup are preserved. Local Java fixtures reach the JAR/AAR/dex/runtime consumers without duplicate compilation; the single GetThis implementation retains the actual base Android behavior. The Unix normalized-copy output and obsolete host-command fixes are verified.
The current source inventory contains 42 Compile items and 41 Android-token-identical migrated files; the earlier author's 40/39 figures are historical receipts, not current inventory proof. These are source models, not measured runtime discovery or independently executed MSBuild. Exact-source build 1625949 succeeds. Installed-SDK, javac/JAR and copy/no-op results remain author-attributed; this review does not claim product or device execution, and the accepted desktop-harness retirement remains intact.
## Summary Retire the experimental `JavaInterop1` binding/Java Callable Wrapper (JCW) generation paths and their obsolete runtime infrastructure. Keep **`XAJavaInterop1`** as 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](#11727), including removal of `ManagedPeer`, `GCUserPeerable`, the handwritten Java.Interop proxies, and `java-interop.jar`. Preserve managed-object transport and consolidate exception transport onto the existing generated `Android.Runtime.JavaProxyThrowable` for both CoreCLR and NativeAOT. The branch is rebased onto `main` commit `058e71b5867c63fd92a00ae545c7899f2b60ed6e`, including the Android test relocation in [#13019](#13019) and the retained-typemap R8 pipeline in [#12829](#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 experimental `JavaInterop1` shape. This PR preserves that supported code-generation path. NativeAOT previously selected `JavaInterop1` for its JCWs. Android NativeAOT has remained an experimental runtime option, documented by [XA1040](https://github.com/dotnet/android/blob/e2230e9d5d/Documentation/docs-mobile/messages/xa1040.md), and **merged [#12133](#12133 already switched it to `XAJavaInterop1` and unified the Android GC bridge around `mono.android.IGCUserPeer`. Before this PoC, the SDK defaults already selected `XAJavaInterop1` for both `AndroidCodegenTarget` and `_AndroidJcwCodegenTarget`. The old `JavaInterop1` option was explicitly documented as **experimental** in the [build-property documentation](https://github.com/dotnet/android/blob/e2230e9d5d/Documentation/docs-mobile/building-apps/build-properties.md#androidcodegentarget). Its historical NativeAOT-default description is stale after #12133 and is corrected here. The supported Android paths have already migrated: - [#12133](#12133) migrated NativeAOT's JCW shape and GC bridge. - [#12152](#12152) removed the built-in proxies' dependency on `ManagedPeer` for native registration. - [#12887](#12887) made the trimmable typemap the default and rejected legacy typemap selection. - [#12890](#12890) removed the old native typemap/reflection-backed Android activation path. - [#13018](#13018) removed the remaining reflection-backed JNI managers. Current Android runtimes use the trimmable managers. All these prerequisites are merged. No supported current Android code-generation or activation path needs `ManagedPeer.construct` or 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 | Removed component | Android justification / replacement | |---|---| | `Java.Interop.ManagedPeer` and `net.dot.jni.ManagedPeer` | No supported generated JCW shape needs their reflective constructor/native-registration trampoline after #12133 and the trimmable migration. Remove initialization, feature-switch plumbing, built-in mappings, scanner exemptions, suppressions, and ProGuard retention together. | | `net.dot.jni.GCUserPeerable`, `jiAddManagedReference`, and `jiClearManagedReferences` | Android GC processing uses `mono.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.JavaProxyObject` and `net.dot.jni.internal.JavaProxyObject` | No consumer remains in the active Android value-manager path. Keep the existing `TrimmableJavaProxyObject` and its Java identity semantics; the removed handwritten class is not its implementation. | | `Java.Interop.JavaProxyThrowable` and `net.dot.jni.internal.JavaProxyThrowable` | NativeAOT still used this exception-transport path, so it is **replaced before removal**, not simply deleted as unused. Route NativeAOT through the existing generated `Android.Runtime.JavaProxyThrowable`, as CoreCLR already did. No third `TrimmableJavaProxyThrowable` is introduced. | | `java-interop.jar` | After deleting its obsolete helpers, it has no remaining supported Android runtime content. Remove build/install wiring and references. Android fixture compilation references `android.jar` and `java_runtime.jar` instead. | | Handwritten `SelfRegistration`, `TestType`, `TestTypeTests`, and the old Java.Interop `CallNonvirtualDerived2` fixture | These were unused or depended on the retired Java-to-managed constructor activation path; the obsolete test class was already excluded by capability guards. Keep the separate live generated-binding class with the same short name and preserve supported constructor/dispatch/exception scenarios. | The removed managed runtime implementation types are internal. Public `JavaObject`, `JavaException`, `JniPeerMembers`, and `IJavaPeerable` remain. 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`: `JavaObject` allocates the Java object, associates the managed peer, then invokes the Java constructor through `StartCreateInstance`/`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 removed `JavaInterop1` enum value. No reflective compatibility shim is reintroduced. ## Implementation changes ### Generator and build surface - Remove `CodeGenerationTarget.JavaInterop1`, `JavaPeerStyle.JavaInterop1`, their generator/source-writer/JCW branches, the `javainterop1` CLI selector, and unused SDK configuration selecting that shape. - Preserve `XAJavaInterop1` output and the independent trimmable pipeline. This is not a rename of the typemap generator to another `AndroidCodegenTarget` value. - Reject retired `AndroidCodegenTarget` values with actionable **XA4232** diagnostics for application and library builds. Retain the existing **XA4240** rejection for retired `_AndroidJcwCodegenTarget` values, and test rejection rather than silently falling back. - Remove retired expected-output trees and conditional `JAVA_INTEROP1`/`TODO_JAVA_INTEROP1`/`TryJavaInterop1` scaffolding. Move shared XML inputs/support sources from `expected.ji` into `TestInputs` so supported tests do not depend on a retired baseline directory. - Update directly related build-property/error documentation and the test catalog. ### Shared Android exception handling Add a shared internal `AndroidExceptionManager` and make CoreCLR and NativeAOT delegate wrapping/unwrapping to it. `JniEnvironment.Exceptions.Throw(Exception)` dispatches to the active runtime's `RaisePendingException` implementation. 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`, or `IJavaPeerable` return 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.RaisePendingException` still throws an existing `JavaException`. Android overrides supply managed-exception wrapping; a runtime without an implementation receives an explicit `NotSupportedException` instead of silently depending on a deleted proxy. The exception contract is documented. ### Finish removing associated scaffolding - **R8 roots:** remove eight keep entries for the four retired Java helpers from `proguard_typemap.cfg` and `proguard_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. - **Registration metadata:** retain `JniAddNativeMethodRegistrationAttribute` with warning-only `[Obsolete]`, explicitly stating that it has no effect. Delete the unused public `JniRuntime.CreationOptions.JniAddNativeMethodRegistrationAttributePresent` property rather than retaining a no-op setting; record its accessor removals with `*REMOVED*` entries in `PublicAPI.Unshipped.txt`. Its unread internal copy and Android assignment are also gone. - **Dead scanner/diagnostic code:** remove the attribute-specific metadata pre-scan/flag, unused checker and commented rejection block, and unused **XA4251** logger plumbing. Remove XA4251 from the English resource and its designer accessor. Other live metadata matching helpers remain. - **AOT profile:** delete the unreferenced `custom.aotprofile` ProjectTools fixture containing removed `ManagedPeer.Init`/`.cctor` records. 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 `.resx` or 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 its `AndroidJavaSource` exclusion to prevent duplicate compilation. Apply fixture deletions and `IGCUserPeer` conversions at the relocated paths; do not restore the external test source directory/globs. Keep the Android-specific `GetThis` implementation 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 - **All 23** former `expected.ji` integration scenarios retain XA baselines and active runners. Restore `Java_Lang_Object` against XA and refresh its previously unused snapshot. Shared inputs are migrated, not deleted. - Retained Common/XA/NRT baselines cover 71 of 72 retired unit-baseline names; the unmatched `WriteInterfaceFields` snapshot already had no test consumer before cleanup. - Existing Android `ConstructorActivationTests`, generated-binding `BindingTests.VirtualMethodBinding`, and `ExceptionTests.ManagedJavaManaged_FinallyExecuted` retain activation, multi-level dispatch, and managed/Java/managed exceptions with Java `finally` execution. - Port array-mutation scenarios to a real Java fixture and generated XA bindings: `int[]`, `int[][]`, `int[][][]`, with null/invalid/success results and complete copy-back assertions. These exercise generated `JNIEnv.CopyArray`, not handwritten replacement marshalling. - Add GC-peer contract coverage for surviving handwritten fixtures and the active managed-object proxy, invoking actual add/clear operations. - Add/strengthen exception identity, causes, unregistered throwable, Java-visible stack trace, callback identity, typed lookup, and throwable-array coverage. - Add **17 permanent regressions** for typed/unboxed lookup and transferred-reference failures. All 17 passed on both runtimes after rebase, without skips. - Require the generated Android throwable in both runtimes' packaging/R8 tests and reject old proxies. Existing R8 response tests now also reject all four obsolete helper roots in both rule modes. 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. | Command/check | Result | |---|---| | `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/jar` and the same command with `-c Release` | **Passed**, 0 warnings/errors in both configurations; public API removal records accepted. | | `bin/Debug/dotnet/dotnet build src/Mono.Android/Mono.Android.csproj -c Debug -v quiet` | **Passed**, 0 errors; no remaining source consumer of the deleted option. | | `bin/Debug/dotnet/dotnet build build-tools/scripts/Prepare.proj -t:BuildDotNet -c Debug -v quiet` with Debug workload roots | **Passed**, 0 errors; existing warnings. An initial overlapping scanner-test build locked a shared intermediate DLL (MSB4018); the SDK retry passed after that build completed, without source workarounds. | | `bin/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'` | **73 passed, 4 existing prerequisite skips, 0 failures**. The filter uses the literal `|` OR operator. | | `bin/Debug/dotnet/dotnet build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug -v quiet` | **Passed**, 0 errors; rebuilt task/resource and ProjectTools consumers. | | Metadata inspection of rebuilt Debug/Release `Java.Interop.dll` and `Xamarin.ProjectTools.dll` | **Passed**: option absent from both configurations, attribute retained with warning-only `Obsolete`, deleted profile absent from manifest resources. | | English resource XML parse, active source/reference checks, `git diff --check` | **Passed**; obsolete roots/diagnostic machinery absent and localization boundaries preserved. | This 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. ```sh bin/Debug/dotnet/dotnet test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll \ --filter 'FullyQualifiedName~R8Tests|Name~RetainedTypeMapRulesDriveR8|Name~TrimmableTypeMap_PackagesAndroidJavaProxyThrowable|Name~BuildProguardEnabledProject' \ -v quiet ``` ### Full post-rebase runtime/device validation Both configuration-specific SDKs were rebuilt after the rebase using `Prepare.proj -t:BuildDotNet -c Debug/Release`. Set matching `DOTNETSDK_WORKLOAD_MANIFEST_ROOTS="$PWD/bin/<configuration>/lib/sdk-manifests"` and `DOTNETSDK_WORKLOAD_PACK_ROOTS="$PWD/bin/<configuration>/lib"`, with `ANDROID_SERIAL=emulator-5554`. Explicit SDK paths avoid `dotnet-local.sh` preferring an existing Release SDK over Debug. ```sh # Debug workload roots, from repository root. bin/Debug/dotnet/dotnet build -t:Install -c Debug \ tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \ -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=CoreCLR \ -p:RuntimeIdentifier=android-arm64 '-p:AdbTarget=-s emulator-5554' -v quiet ( cd tests/Mono.Android-Tests/Mono.Android-Tests ../../../bin/Debug/dotnet/dotnet test Mono.Android.NET-Tests.csproj \ --no-build -c Debug --device emulator-5554 --report-trx \ --results-directory ../../../bin/TestDebug/RebaseResults \ -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=CoreCLR \ -p:RuntimeIdentifier=android-arm64 '-p:AdbTarget=-s emulator-5554' ) ``` **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. ```sh # Release workload roots, from repository root. bin/Release/dotnet/dotnet build -t:Install -c Release \ tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \ -m:1 -p:BuildInParallel=false \ -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=NativeAOT \ -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \ -p:_AndroidEnableTypemapR8Trimming=true -p:_SkipNdkResolution=false \ '-p:AdbTarget=-s emulator-5554' -v quiet ( cd tests/Mono.Android-Tests/Mono.Android-Tests ../../../bin/Release/dotnet/dotnet test Mono.Android.NET-Tests.csproj \ --no-build -c Release --device emulator-5554 --report-trx \ --results-directory ../../../bin/TestRelease/RebaseResults \ -p:BuildInParallel=false \ -p:AndroidTypeMapImplementation=trimmable -p:AndroidRuntime=NativeAOT \ -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \ -p:_AndroidEnableTypemapR8Trimming=true -p:_SkipNdkResolution=false \ '-p:AdbTarget=-s emulator-5554' ) ``` **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.json` confirmed shrinking enabled. No old native-output-path override or DGML workaround was used, and no trimming/R8/AOT was disabled. Build-only `-m:1` is omitted from the test invocation because MTP rejects the forwarded `--m` option. 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 | Command/check | Recorded result | |---|---| | `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/jar` | **478 passed**, 0 skipped. | | Same command shape for `external/Java.Interop/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj` | **50 passed**, 0 skipped. | | Same command shape for `external/Java.Interop/tests/Java.Interop.Tools.Generator-Tests/Java.Interop.Tools.Generator-Tests.csproj` | **18 passed**, 0 skipped. | | `bin/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~GenerateTrimmableTypeMapTests` | **16 passed**, 0 skipped after rebase; superseded by the broader latest 73-case run. | | `bin/Debug/dotnet/dotnet test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~InvalidConfigTests\|Name~TrimmableTypeMap_PackagesAndroidJavaProxyThrowable\|Name~BuildProguardEnabledProject\|Name~RetainedTypeMapRulesDriveR8' -v quiet` | **58 passed**, 0 skips/failures after rebase; latest related resource/packaging coverage is the 36-case run above. | Generated-XA JCW device commands used `tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj` with `build -t:Install`, then matching `test --no-build` from its directory, `--device emulator-5554 --report-trx`, and configuration-specific workload roots: - **CoreCLR:** `-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. - **NativeAOT arrays:** `-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.jar` is 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 - Old Java wrappers/handwritten code explicitly using the retired experimental `ManagedPeer`/JavaInterop1 contract must be regenerated/migrated. No compatibility shim is retained. [#11635](#11635) tracks that question; #11727 documents the removal direction. Normal .NET for Android/MAUI codegen remains intact. - The unused public `JniAddNativeMethodRegistrationAttributePresent` configuration 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. - Developers should clean old intermediates when changing between pre-/post-removal trees. No permanent build invalidation/cleanup machinery is added solely for this transition. - Broader single-target generator-plumbing simplification and other legacy public API deprecations/removals are separate follow-ups, not mixed into this PR. - Successful shared exception lookup could additionally receive borrowed/global-reference success tests, complementing the existing failure matrix; this optional coverage is not claimed as implemented. ## Related issues and PRs - [#11727](#11727): experimental JavaInterop1 retirement tracker. - [#11635](#11635): old ManagedPeer bytecode compatibility discussion. - [#12133](#12133), [#12152](#12152): merged NativeAOT/GC migration and proxy decoupling. - [#12887](#12887), [#12890](#12890): merged trimmable default and legacy activation/native typemap removal. - [#13018](#13018): merged reflection-manager removal. - [#13019](#13019): merged Android test relocation, reconciled here. - [#12829](#12829): retained-key R8 pipeline replacing DGML, exercised by validation and preserved by this cleanup. - [#11480](#11480): **closed, unmerged** earlier trimmable-only ManagedPeer removal; useful artifact-check precedent, not a claimed prerequisite. No performance numbers from it are claimed here. - [dotnet/java-interop#1490](dotnet/java-interop#1490): **closed, unmerged** ManagedPeer registration modernization; unnecessary once those Android consumers are removed. ---- 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*. - [x] Links to issues fixed - [x] Unit tests


Core JNI interop tests now run exclusively in the Android device suite. Keeping their sources in the former standalone test directory leaves misleading ownership, external source globs, and separate desktop/Android Java fixture selection. This Android-only relocation puts the sources beside their actual test project and simplifies fixture compilation without changing supported Android test behavior.
Follow-up to the merged #13018. This PR now targets
main; its diff contains only the Android test relocation and cleanup. It preserves the removal of the desktop harness, reflection-backed JNI managers, and obsoleteJavaVMFixturescaffolding.tests/Mono.Android-Tests/Java.Interop-Tests/; use the project's defaultCompileglob and a localTestJarEntryglob. Preserve the existing exclusion for tests of the internalJNIEnvlayout.__ANDROID__branches while retaining their Android assertions. Remove unsupported desktop-only custom-type-manager remapping tests and unused helper fixtures. PreserveNO_MARSHAL_MEMBER_BUILDER_SUPPORT,NO_GC_BRIDGE_SUPPORT, and applicable runtime/AOT exclusions.GetThis.javaonto the Android-safe implementation for every typemap. Remove duplicatejava-trimmableselection and unused desktop test targets, and prevent implicitAndroidJavaSourcecompilation from duplicating fixture classes in the JAR.Migration guidance: Core JNI test additions belong in the new Android directory, including C# fixtures such as
ThrowableInputCleanupTests.csandJavaPeerControlBlockTests.cs, and Java fixtures underjava/net/dot/jni/test/. Preserve coverage introduced by #13014 and #13015 when moving or updating these files. Other branches touching the old directory, including #13016, should rebase into the new location. Do not recreate the removed standalone desktop test project.Validation
The following results were recorded during implementation. The installed-SDK build passed, but repository-SDK/device coverage remains blocked; marking this PR ready for review does not imply that coverage passed.
dotnet build tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj -c Releasedotnet msbuild tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj -p:Configuration=Release -getItem:Compile,TestJarEntry,AndroidJavaSourcejavac --release 17 -d <session-artifacts>/java-fixture-classes tests/Mono.Android-Tests/Java.Interop-Tests/java/net/dot/jni/test/*.java external/Java.Interop/src/Java.Interop/java/net/dot/jni/GCUserPeerable.java external/Java.Interop/src/Java.Interop/java/net/dot/jni/ManagedPeer.javajar tf tests/Mono.Android-Tests/Java.Interop-Tests/Jars/Mono.Android-Test-classes-trimmable.jarandjavap -c -classpath tests/Mono.Android-Tests/Java.Interop-Tests/Jars/Mono.Android-Test-classes-trimmable.jar net.dot.jni.test.GetThisgit diff HEAD^ HEAD --checkmake prepare CONFIGURATION=ReleaseMicrosoft.NETCore.App.Refversion10.0.13was unavailable in the configured feeds. The corresponding nuget.org package URL also returned HTTP 404.make all CONFIGURATION=Releasebin/BuildRelease/net10.0/xa-prep-tasks.dllafter preparation failed../dotnet-local.sh build -t:Install -c Release tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -p:AndroidTypeMapImplementation=llvm-ir./dotnet-local.sh build -t:Install -c Release tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -p:AndroidTypeMapImplementation=trimmableNo on-device tests were run because the required local SDK/install builds failed, despite a connected arm64 emulator. Mono/CoreCLR/NativeAOT runtime behavior and both typemap device lanes remain unvalidated. No desktop JVM validation is required.
Pull Request
title and
description
should follow the
commit-messages.mdworkflow documentation, and in particular should include:No issue is closed by this relocation. Existing Android tests compile; on-device test execution is blocked by the local SDK prerequisites described above.