[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[TrimmableTypeMap] Enable JNI replacement APIs - #11270

Merged
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements
May 11, 2026
Merged

[TrimmableTypeMap] Enable JNI replacement APIs#11270
simonrozsival merged 5 commits into
mainfrom
dev/simonrozsival/trimmable-replacements

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

  • share JNI remapping replacement lookup between the native and trimmable typemap type managers
  • enable replacement type and method lookup for TrimmableTypeMapTypeManager
  • preserve remapping counts across incremental builds when remap native-code generation is skipped
  • re-enable the CoreCLRTrimmable Java.Interop replacement tests

Implementation note

The shared replacement lookup logic lives in dotnet/android as JniRemappingLookup rather than moving into the common Java.Interop type-manager base class. The base layer is in the external/Java.Interop submodule, while this lookup depends on Android-specific runtime state and native entry points (JNIEnvInit.jniRemappingInUse, _monodroid_lookup_replacement_type, and _monodroid_lookup_replacement_method_info). Keeping the helper in this repo avoids a Java.Interop/submodule change while still sharing the logic between AndroidTypeManager and TrimmableTypeMapTypeManager.

Test Plan

  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=Build_WithTrimmableTypeMap_RemappingCountsAreInApplicationConfig" -- NUnit.NumberOfTestWorkers=1
  • CoreCLRTrimmable Mono.Android.NET-Tests device lane: 887 total, 0 failures
  • Mono Mono.Android.NET-Tests device lane: 273 total, 0 failures
  • CoreCLR Mono.Android.NET-Tests device lane: 273 total, 0 failures

Rebased on main.

Closes#10968

Related issues

CopilotAI review requested due to automatic review settings May 2, 2026 13:49

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends JNI remapping support to the trimmable typemap runtime path and updates the build/runtime plumbing so remapping metadata can be reused across native and trimmable type managers. It fits into the ongoing trimmable typemap work by bringing replacement-type/method handling closer to feature parity with the existing runtime path.

Changes:

  • Extract shared JNI remapping lookup logic into a new JniRemappingLookup helper and wire it into both runtime type manager implementations.
  • Pass remapping XML into native application-config generation and add a build test for preserving remapping counts across an incremental build where remap native code generation is skipped.
  • Re-enable previously excluded Java.Interop remapping tests for the trimmable typemap lane.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csRemoves trimmable-only exclusions for Java.Interop remapping tests.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targetsPasses remapping XML into native app-config generation.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csAdds incremental-build coverage for remapping counts in application config.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.csFalls back to reading remap metadata directly when task-object state is unavailable.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.csExtracts reusable XML parsing/counting logic for remapping metadata.
src/Mono.Android/Mono.Android.csprojIncludes the new shared remapping lookup source file.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csEnables replacement type/method and desugar fallback lookup for trimmable typemap.
src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.csReuses the shared fallback-type helper.
src/Mono.Android/Microsoft.Android.Runtime/JniRemappingLookup.csIntroduces shared native remapping lookup helpers.
src/Mono.Android/Android.Runtime/AndroidRuntime.csReplaces duplicated AndroidTypeManager remapping logic with shared helper calls.

Comment threadsrc/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Outdated
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from d530869 to 9eb074fCompareMay 2, 2026 14:33
@simonrozsival
simonrozsival changed the base branch from trimmable-typemap-startup-fixes to mainMay 2, 2026 14:33
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 9eb074f to 8c1d5a2CompareMay 2, 2026 14:48
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels May 2, 2026
@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Clean refactoring with a subtle correctness fix

Summary: Extracts shared JNI remapping logic from AndroidTypeManager into a new JniRemappingLookup helper class, reuses it across all three type managers, and enables replacement type/method lookup for TrimmableTypeMapTypeManager. Well-structured and minimal diff.

Positive callouts:

  • The shared JniRemappingLookup eliminates three copies of the desugar-type construction and provides a single point of truth
  • Nullable improvements on the interop struct fields (string? + explicit validation) are a correctness win over the old non-nullable fields that Marshal.PtrToStructure could silently return null for
  • Typo fix: "one one of""one of" in the log message
  • Test exclusion removal is consistent with the feature enablement

One item to document: The shared code appends $_CC to the first fallback type (DesugarFoo$_CC), while the old AndroidTypeManager returned just DesugarFoo. This aligns all three type managers and is likely the correct behavior for D8/R8 companion classes, but worth noting in the commit message since it's a behavioral change beyond pure refactoring.

SeverityCount
💡 Suggestion3

CI: license/cla ✅, dotnet-android (public) ✅. Internal Xamarin.Android-PR pipeline status not visible from public checks — maintainer should verify.

Generated by Android PR Reviewer for issue #11270 · ● 4.9M

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK merging this, but I wonder if we should manually test an Intune app?

Share JNI remapping lookup between native and trimmable typemap type managers and enable replacement type and method lookup for TrimmableTypeMapTypeManager.
Re-enable the CoreCLRTrimmable Java.Interop replacement tests covered by the implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the dev/simonrozsival/trimmable-replacements branch from 8c1d5a2 to f0fa817CompareMay 4, 2026 18:32
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I wonder if we should manually test an Intune app?

I don't think I can test it on my phone because it is not enrolled as a corporate device. Or maybe that's not necessary and a local build of an app with InTune integration would be fine? I will test tomorrow and I'll report if everything worked with all type map implementations.

simonrozsivaland others added 2 commits May 7, 2026 07:48
…mmable-replacements
# Conflicts:
#	tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
GenericHolder and open generic construction tests are now enabled by main, so keep them enabled on the JNI replacement branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival removed the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 7, 2026
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

I am OK merging this, but I wonder if we should manually test an Intune app?

Done — validated on an Android emulator (API 36, arm64-v8a) using this branch's local SDK. All three runtime/typemap configurations pass:

#RuntimeTypemapBuildLaunchJniRemappingLookup "Remapping method" log linesDisplayed
1Monollvm-irMAMApplication.onCreate → onMAMCreate, AppCompatActivity.onCreate → onMAMCreate+779 ms
2CoreCLRllvm-ir✅ same+838 ms
3CoreCLRtrimmable✅ same, via _IntuneSmoke.TypeMap.dllTrimmableTypeMapTypeManager+886 ms

Log excerpt from config 3 (the path this PR enables):

D monodroid-assembly: Mapped: ... name == '_IntuneSmoke.TypeMap.dll'
D monodroid-assembly: Remapping method `com/microsoft/intune/mam/client/app/MAMApplication.onCreate()V`
to `com/microsoft/intune/mam/client/app/MAMApplication.onMAMCreate()V`
I IntuneSmoke: Application.OnCreate; type=IntuneSmoke.MainApplication; base=Android.App.Application
D monodroid-assembly: Remapping method `androidx/appcompat/app/AppCompatActivity.onCreate(Landroid/os/Bundle;)V`
to `androidx/appcompat/app/AppCompatActivity.onMAMCreate(Landroid/os/Bundle;)V`
I IntuneSmoke: MainActivity.OnCreate; type=IntuneSmoke.MainActivity; base=AndroidX.AppCompat.App.AppCompatActivity
I ActivityTaskManager: Displayed com.xamarin.microsoftintune/...MainActivity for user 0: +886ms

Identical "Remapping method" log lines fire under all three configs — direct evidence that the shared JniRemappingLookup helper works the same way through both AndroidTypeManager (configs 1+2) and the new TrimmableTypeMapTypeManager (config 3).

Bonus finding — Intune NuGet net11 workaround

Microsoft.Intune.Maui.Essentials.android 11.5.1 ships per-TFM assets only under net8.0-android/net9.0-android and its MamifyFiles task errors with Unsupported TargetFramework net11.0-android. Decompiling Core.dll shows the check is just File.Exists("Resources/<TFM>/classes.jar"):

publicstaticstringGetInternalMAMSDK(stringtargetFramework){stringfullPath=GetFullPath(Path.Combine("Resources",targetFramework,"classes.jar"));if(!File.Exists(fullPath))thrownewException("Unsupported TargetFramework "+targetFramework);returnfullPath;}

The Java assets are TFM-agnostic, so symlinking the cached net9.0-android directories to net11.0-android is sufficient to build and run Intune apps on this branch:

IP=~/.nuget/packages/microsoft.intune.maui.essentials.android/11.5.1
forsubin build/netstandard2.0/Resources build/netstandard2.0/BuildTool aar;do
ln -s "$IP/$sub/net9.0-android""$IP/$sub/net11.0-android"done

This is a hint for the Intune team (cc /cc relevant folks on #8548) — they could mirror the net9.0-android directory to net10.0-android/net11.0-android in a future package to unblock .NET 10+ consumers with zero functional changes. The IntuneValidateAndroidBuild=false MSBuild flag handles the soft warning at line 146 of their targets but does not bypass the hard File.Exists check.

Related test improvements (separate commit, not yet pushed)

While I was at it I also (a) bumped Microsoft_Intune_Maui_Essentials_android in KnownPackages.cs from 10.0.0-beta211.5.1, and (b) converted the long-Assert.Ignored MicrosoftIntune test in InstallAndRunTests.cs to TestCaseSource with a _AndroidTypeMapImplementation matrix that includes a (Release, "trimmable", CoreCLR) row covering this PR's path. Assert.Ignore is kept (with the comment updated to the actual current blocker — Intune still doesn't ship net11.0-android assets) so the test stays skipped on main; deleting one line activates full automated coverage once Intune publishes net10/net11 assets. Happy to push that as part of this PR or a follow-up — let me know which you prefer.

TL;DR: 👍 to merge as far as Intune is concerned.

@simonrozsival

Copy link
Copy Markdown
MemberAuthor

CI failure is unrelated

@simonrozsival
simonrozsival merged commit f6b5eb0 into mainMay 11, 2026
2 of 3 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/trimmable-replacements branch May 11, 2026 11:16
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 11, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thistrimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Unify InTune/method replacement support between AndroidTypeManager and TrimmableTypeMapTypeManager

3 participants

@simonrozsival@jonathanpeppers