Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
be31f95
[TrimmableTypeMap] Root manifest-referenced types as unconditional
simonrozsival Mar 27, 2026
c0126d7
Address PR review: fix manifest name matching and null guard
simonrozsival Mar 31, 2026
023a765
Document IsUnconditional mutation contract: only set to true
simonrozsival Apr 1, 2026
431f59e
Fix RootManifestReferencedTypes: remove IO, add relative name resolution
simonrozsival Apr 1, 2026
ade15c9
[TrimmableTypeMap] Root Application and Instrumentation types
simonrozsival Apr 7, 2026
6ec6832
[TrimmableTypeMap] Match compat names in manifest rooting
simonrozsival Apr 7, 2026
4568e1a
[TrimmableTypeMap] Add coded warning for unresolved types
simonrozsival Apr 7, 2026
e9d0850
[TrimmableTypeMap] Track manifest-related target inputs
simonrozsival Apr 7, 2026
483dc54
[TrimmableTypeMap] Use ILogger for generator diagnostics
simonrozsival Apr 7, 2026
ff76bf5
[TrimmableTypeMap] Use switch expression for manifest names
simonrozsival Apr 7, 2026
f26a757
Revert "[TrimmableTypeMap] Use ILogger for generator diagnostics"
simonrozsival Apr 7, 2026
1fcdb5b
Revert "[TrimmableTypeMap] Track manifest-related target inputs"
simonrozsival Apr 7, 2026
b9398e9
[TrimmableTypeMap] Reuse existing manifest name helpers
simonrozsival Apr 7, 2026
9b668cf
[TrimmableTypeMap] Merge manifest matching tests into theory
simonrozsival Apr 7, 2026
b89d96f
[TrimmableTypeMap] Match LogCodedWarning callback shape
simonrozsival Apr 7, 2026
cbe4b30
[TrimmableTypeMap] Fix theory package name
simonrozsival Apr 7, 2026
9449cf6
[TrimmableTypeMap] Add typed logger interface
simonrozsival Apr 7, 2026
1940373
Drop generic log callback from type map generator
simonrozsival Apr 7, 2026
3bd5710
Restore typed typemap info logging
simonrozsival Apr 7, 2026
860901d
Fix manifest-rooting resolution and deferred registration
simonrozsival Apr 7, 2026
d235634
Add Android.Runtime.TrimmableNativeRegistration to Mono.Android
simonrozsival Apr 7, 2026
900641d
Enable trimmable typemap for Mono.Android.NET-Tests
simonrozsival Apr 7, 2026
8e059cb
Address review feedback on trimmable typemap runtime
simonrozsival Apr 8, 2026
f5f7485
Remove TrimmableNativeRegistration wrapper
simonrozsival Apr 8, 2026
80785bd
Inline activation ctor in UCO wrappers, remove ActivateInstance
simonrozsival Apr 8, 2026
6c5a8b5
Exclude NativeTypeMap tests for trimmable, add safety guards
simonrozsival Apr 8, 2026
ecb19dc
Skip nctor emission in JCW for open generic type definitions
simonrozsival Apr 8, 2026
0b0ea95
Add WithinNewObjectScope guard to UCO constructors
simonrozsival Apr 8, 2026
a4bb2ad
Fix generic type activation: emit no-op UCO for open generics
simonrozsival Apr 8, 2026
8cda06d
Fix open generic UCO: throw NotSupportedException via JNI
simonrozsival Apr 8, 2026
8eec472
Revert generic UCO to no-op, exclude NewOpenGenericTypeThrows
simonrozsival Apr 8, 2026
95ab1ae
Fix ExcludeCategories ordering for trimmable typemap
simonrozsival Apr 8, 2026
46278d5
Fix JavaCast validation, exclude Java.Interop JavaObjectTest for trim…
simonrozsival Apr 8, 2026
0ff600e
Exclude Java.Interop-Tests fixtures and 2 trimmable-incompatible tests
simonrozsival Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/docs-mobile/messages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
+ [XA4247](xa4247.md): Could not resolve POM file for artifact '{artifact}'.
+ [XA4248](xa4248.md): Could not find NuGet package '{nugetId}' version '{version}' in lock file. Ensure NuGet Restore has run since this `<PackageReference>` was added.
+ [XA4235](xa4249.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id:version'.
+ [XA4250](xa4250.md): Manifest-referenced type '{type}' was not found in any scanned assembly. It may be a framework type.
+ XA4300: Native library '{library}' will not be bundled because it has an unsupported ABI.
+ [XA4301](xa4301.md): Apk already contains the item `xxx`.
+ [XA4302](xa4302.md): Unhandled exception merging \`AndroidManifest.xml\`: {ex}
Expand Down
33 changes: 33 additions & 0 deletions Documentation/docs-mobile/messages/xa4250.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: .NET for Android warning XA4250
description: XA4250 warning code
ms.date: 04/07/2026
f1_keywords:
- "XA4250"
---

# .NET for Android warning XA4250

## Example message

Manifest-referenced type '{0}' was not found in any scanned assembly. It may be a framework type.

```text
warning XA4250: Manifest-referenced type 'com.example.MainActivity' was not found in any scanned assembly. It may be a framework type.
```

## Issue

The build found a type name in `AndroidManifest.xml`, but it could not match that name to any Java peer discovered in the app's managed assemblies.

This can be expected for framework-provided types, but it can also indicate that the manifest entry does not match the name generated for a managed Android component.

## Solution

If the manifest entry refers to an Android framework type, this warning can usually be ignored.

Otherwise:

1. Verify the `android:name` value in the manifest.
2. Ensure the managed type is included in the app build.
3. Check for namespace, `[Register]`, or nested-type naming mismatches between the manifest and the managed type.
12 changes: 11 additions & 1 deletion build-tools/automation/yaml-templates/stage-package-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,20 @@ stages:
testName: Mono.Android.NET_Tests-CoreCLR
project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj
testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)CoreCLR.xml
extraBuildArgs: -p:TestsFlavor=CoreCLR -p:UseMonoRuntime=false
extraBuildArgs: -p:MonoAndroidTypeMapFlavor=legacy
artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab
artifactFolder: $(DotNetTargetFramework)-CoreCLR

- template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml
parameters:
configuration: $(XA.Build.Configuration)
testName: Mono.Android.NET_Tests-CoreCLRTrimmable
project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj
testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)CoreCLRTrimmable.xml
extraBuildArgs: -p:MonoAndroidTypeMapFlavor=trimmable
artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab
artifactFolder: $(DotNetTargetFramework)-CoreCLRTrimmable

- template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml
parameters:
configuration: $(XA.Build.Configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer)
PropertyMapper.ApplyMappings (app, component.Properties, PropertyMapper.ApplicationElementMappings);
}

internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer)
internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer, string packageName)
{
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
var element = new XElement ("instrumentation",
Expand All @@ -176,6 +176,9 @@ internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer)
return;
}
PropertyMapper.ApplyMappings (element, component.Properties, PropertyMapper.InstrumentationMappings);
if (element.Attribute (AndroidNs + "targetPackage") is null && !string.IsNullOrEmpty (packageName)) {
element.SetAttributeValue (AndroidNs + "targetPackage", packageName);
}

manifest.Add (element);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,28 @@ static void WriteClassDeclaration (JavaPeerInfo type, TextWriter writer)

static void WriteStaticInitializer (JavaPeerInfo type, TextWriter writer)
{
string className = JniSignatureHelper.GetJavaSimpleName (type.JavaName);

// Application and Instrumentation types cannot call registerNatives in their
// static initializer — the native library isn't loaded yet at that point.
// Their registerNatives call is emitted in the generated
// ApplicationRegistration.registerApplications() method instead.
// static initializer — the runtime isn't ready yet at that point. Emit a
// lazy one-time helper instead so the first managed callback can register
// the class just before invoking its native method.
if (type.CannotRegisterInStaticConstructor) {
writer.Write ($$"""
private static boolean __md_natives_registered;
private static synchronized void __md_registerNatives ()
{
if (!__md_natives_registered) {
mono.android.Runtime.registerNatives ({{className}}.class);
__md_natives_registered = true;
}
}


""");
return;
}

string className = JniSignatureHelper.GetJavaSimpleName (type.JavaName);
writer.Write ($$"""
static {
mono.android.Runtime.registerNatives ({{className}}.class);
Expand All @@ -154,7 +167,17 @@ static void WriteConstructors (JavaPeerInfo type, TextWriter writer)
public {{simpleClassName}} ({{parameters}})
{
super ({{superArgs}});

""");

if (!type.CannotRegisterInStaticConstructor) {
writer.Write ($$"""
if (getClass () == {{simpleClassName}}.class) nctor_{{ctor.ConstructorIndex}} ({{args}});

""");
}

writer.Write ($$"""
}


Expand Down Expand Up @@ -197,6 +220,10 @@ static void WriteFields (JavaPeerInfo type, TextWriter writer)

static void WriteMethods (JavaPeerInfo type, TextWriter writer)
{
string registerNativesLine = type.CannotRegisterInStaticConstructor
? "\t\t__md_registerNatives ();\n"
: "";

foreach (var method in type.MarshalMethods) {
if (method.IsConstructor) {
continue;
Expand All @@ -222,7 +249,7 @@ static void WriteMethods (JavaPeerInfo type, TextWriter writer)
@Override
public {{javaReturnType}} {{method.JniName}} ({{parameters}}){{throwsClause}}
{
{{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
{{registerNativesLine}} {{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
}
public native {{javaReturnType}} {{method.NativeCallbackName}} ({{parameters}});

Expand All @@ -233,7 +260,7 @@ static void WriteMethods (JavaPeerInfo type, TextWriter writer)

{{access}} {{javaReturnType}} {{method.JniName}} ({{parameters}}){{throwsClause}}
{
{{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
{{registerNativesLine}} {{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
}
{{access}} native {{javaReturnType}} {{method.NativeCallbackName}} ({{parameters}});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class ManifestGenerator
}

if (peer.ComponentAttribute.Kind == ComponentKind.Instrumentation) {
ComponentElementBuilder.AddInstrumentation (manifest, peer);
ComponentElementBuilder.AddInstrumentation (manifest, peer, PackageName);
continue;
}

Expand Down Expand Up @@ -116,10 +116,7 @@ class ManifestGenerator
}

// Apply manifest placeholders
string? placeholders = ManifestPlaceholders;
if (placeholders is not null && placeholders.Length > 0) {
ApplyPlaceholders (doc, placeholders);
}
ApplyPlaceholders (doc, ManifestPlaceholders);

return (doc, providerNames);
}
Expand Down Expand Up @@ -250,8 +247,12 @@ XElement CreateRuntimeProvider (string name, string? processName, int initOrder)
/// Replaces ${key} placeholders in all attribute values throughout the document.
/// Placeholder format: "key1=value1;key2=value2"
/// </summary>
static void ApplyPlaceholders (XDocument doc, string placeholders)
internal static void ApplyPlaceholders (XDocument doc, string? placeholders)
{
if (placeholders.IsNullOrEmpty ()) {
return;
}

var replacements = new Dictionary<string, string> (StringComparer.Ordinal);
foreach (var entry in placeholders.Split (PlaceholderSeparators, StringSplitOptions.RemoveEmptyEntries)) {
var eqIndex = entry.IndexOf ('=');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ sealed record UcoMethodData
/// An [UnmanagedCallersOnly] static wrapper for a constructor callback.
/// Signature must match the full JNI native method signature (jnienv + self + ctor params)
/// so the ABI is correct when JNI dispatches the call.
/// Body: TrimmableNativeRegistration.ActivateInstance(self, typeof(TargetType)).
/// Body: TrimmableTypeMap.ActivateInstance(self, typeof(TargetType)).
/// </summary>
sealed record UcoConstructorData
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ static JavaPeerProxyData BuildProxyType (JavaPeerInfo peer, HashSet<string> used
{
// Use managed type name for proxy naming to guarantee uniqueness across aliases
// (two types with the same JNI name will have different managed names).
var proxyTypeName = peer.ManagedTypeName.Replace ('.', '_').Replace ('+', '_') + "_Proxy";
// Replace generic arity markers too, because backticks would make the emitted
// proxy type itself look generic even though we don't emit generic parameters.
var proxyTypeName = peer.ManagedTypeName.Replace ('.', '_').Replace ('+', '_').Replace ('`', '_') + "_Proxy";

// Guard against name collisions (e.g., "My.Type" and "My_Type" both map to "My_Type_Proxy")
if (!usedProxyNames.Add (proxyTypeName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
int typeMethodStart = Metadata.GetRowCount (TableIndex.MethodDef) + 1;

var handle = Metadata.AddTypeDefinition (
TypeAttributes.NestedPrivate | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
TypeAttributes.NestedAssembly | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
default,
Metadata.GetOrAddString ($"__utf8_{size}"),
Metadata.AddTypeReference (SystemRuntimeRef,
Expand All @@ -259,7 +259,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
/// </summary>
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
Action<BlobEncoder> encodeSig, Action<InstructionEncoder> emitIL)
=> EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null);
=> EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null, useBranches: false);

/// <summary>
/// Emits a method body and definition with optional local variable declarations.
Expand All @@ -269,9 +269,14 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
/// <see cref="BlobBuilder"/> and must write the full <c>LOCAL_SIG</c> blob (header 0x07,
/// compressed count, then each variable type).
/// </param>
/// <param name="useBranches">
/// If true, creates a <see cref="ControlFlowBuilder"/> so the emitted IL can use
/// <see cref="InstructionEncoder.DefineLabel"/>, <see cref="InstructionEncoder.Branch"/>,
/// and <see cref="InstructionEncoder.MarkLabel"/>.
/// </param>
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
Action<BlobEncoder> encodeSig, Action<InstructionEncoder> emitIL,
Action<BlobBuilder>? encodeLocals)
Action<BlobBuilder>? encodeLocals, bool useBranches = false)
{
_sigBlob.Clear ();
encodeSig (new BlobEncoder (_sigBlob));
Expand All @@ -287,7 +292,11 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
}

_codeBlob.Clear ();
var encoder = new InstructionEncoder (_codeBlob);
ControlFlowBuilder? cfb = null;
if (useBranches) {
cfb = new ControlFlowBuilder ();
}
var encoder = cfb != null ? new InstructionEncoder (_codeBlob, cfb) : new InstructionEncoder (_codeBlob);
emitIL (encoder);

while (ILBuilder.Count % 4 != 0) {
Expand Down
Loading