Skip to content

Commit 682ebac

Browse files
[TrimmableTypeMap] Manifest rooting and deferred registration propagation (#11097)
* [TrimmableTypeMap] Manifest rooting and deferred registration propagation - Manifest name promotion: promote compat JNI names for manifest-rooted components so the generated JCW class name matches the manifest entry - Deferred registration propagation: propagate CannotRegisterInStaticConstructor to generated managed base types of Application/Instrumentation - Instrumentation targetPackage: pass package name for manifest instrumentation elements - XA4250 warning: warn when manifest references a type that cannot be resolved in any scanned assembly - NestedAssembly for UTF-8 helpers: fix FieldAccessException from NestedPrivate visibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * [TrimmableTypeMap] Address review feedback on manifest rooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c71f0b3 commit 682ebac

12 files changed

Lines changed: 588 additions & 15 deletions

File tree

Documentation/docs-mobile/messages/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
207207
+ [XA4247](xa4247.md): Could not resolve POM file for artifact '{artifact}'.
208208
+ [XA4248](xa4248.md): Could not find NuGet package '{nugetId}' version '{version}' in lock file. Ensure NuGet Restore has run since this `<PackageReference>` was added.
209209
+ [XA4235](xa4249.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id:version'.
210+
+ [XA4250](xa4250.md): Manifest-referenced type '{type}' was not found in any scanned assembly. It may be a framework type.
210211
+ XA4300: Native library '{library}' will not be bundled because it has an unsupported ABI.
211212
+ [XA4301](xa4301.md): Apk already contains the item `xxx`.
212213
+ [XA4302](xa4302.md): Unhandled exception merging \`AndroidManifest.xml\`: {ex}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
title: .NET for Android warning XA4250
3+
description: XA4250 warning code
4+
ms.date: 04/07/2026
5+
f1_keywords:
6+
- "XA4250"
7+
---
8+
9+
# .NET for Android warning XA4250
10+
11+
## Example message
12+
13+
Manifest-referenced type '{0}' was not found in any scanned assembly. It may be a framework type.
14+
15+
```text
16+
warning XA4250: Manifest-referenced type 'com.example.MainActivity' was not found in any scanned assembly. It may be a framework type.
17+
```
18+
19+
## Issue
20+
21+
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.
22+
23+
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.
24+
25+
## Solution
26+
27+
If the manifest entry refers to an Android framework type, this warning can usually be ignored.
28+
29+
Otherwise:
30+
31+
1. Verify the `android:name` value in the manifest.
32+
2. Ensure the managed type is included in the app build.
33+
3. Check for namespace, `[Register]`, or nested-type naming mismatches between the manifest and the managed type.

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ComponentElementBuilder.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ internal static void UpdateApplicationElement (XElement app, JavaPeerInfo peer)
165165
PropertyMapper.ApplyMappings (app, component.Properties, PropertyMapper.ApplicationElementMappings);
166166
}
167167

168-
internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer)
168+
internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer, string packageName)
169169
{
170170
string jniName = JniSignatureHelper.JniNameToJavaName (peer.JavaName);
171171
var element = new XElement ("instrumentation",
@@ -176,6 +176,9 @@ internal static void AddInstrumentation (XElement manifest, JavaPeerInfo peer)
176176
return;
177177
}
178178
PropertyMapper.ApplyMappings (element, component.Properties, PropertyMapper.InstrumentationMappings);
179+
if (element.Attribute (AndroidNs + "targetPackage") is null && !packageName.IsNullOrEmpty ()) {
180+
element.SetAttributeValue (AndroidNs + "targetPackage", packageName);
181+
}
179182

180183
manifest.Add (element);
181184
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestGenerator.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class ManifestGenerator
7373
}
7474

7575
if (peer.ComponentAttribute.Kind == ComponentKind.Instrumentation) {
76-
ComponentElementBuilder.AddInstrumentation (manifest, peer);
76+
ComponentElementBuilder.AddInstrumentation (manifest, peer, PackageName);
7777
continue;
7878
}
7979

@@ -116,10 +116,7 @@ class ManifestGenerator
116116
}
117117

118118
// Apply manifest placeholders
119-
string? placeholders = ManifestPlaceholders;
120-
if (placeholders is not null && placeholders.Length > 0) {
121-
ApplyPlaceholders (doc, placeholders);
122-
}
119+
ApplyPlaceholders (doc, ManifestPlaceholders);
123120

124121
return (doc, providerNames);
125122
}
@@ -250,8 +247,12 @@ XElement CreateRuntimeProvider (string name, string? processName, int initOrder)
250247
/// Replaces ${key} placeholders in all attribute values throughout the document.
251248
/// Placeholder format: "key1=value1;key2=value2"
252249
/// </summary>
253-
static void ApplyPlaceholders (XDocument doc, string placeholders)
250+
internal static void ApplyPlaceholders (XDocument doc, string? placeholders)
254251
{
252+
if (placeholders.IsNullOrEmpty ()) {
253+
return;
254+
}
255+
255256
var replacements = new Dictionary<string, string> (StringComparer.Ordinal);
256257
foreach (var entry in placeholders.Split (PlaceholderSeparators, StringSplitOptions.RemoveEmptyEntries)) {
257258
var eqIndex = entry.IndexOf ('=');

src/Microsoft.Android.Sdk.TrimmableTypeMap/ITrimmableTypeMapLogger.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ public interface ITrimmableTypeMapLogger
1010
void LogGeneratedRootTypeMapInfo (int assemblyReferenceCount);
1111
void LogGeneratedTypeMapAssembliesInfo (int assemblyCount);
1212
void LogGeneratedJcwFilesInfo (int sourceCount);
13+
void LogUnresolvedTypeWarning (string name);
14+
void LogRootingManifestReferencedTypeInfo (string name, string managedTypeName);
1315
}

src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ public sealed record JavaPeerInfo
1212
{
1313
/// <summary>
1414
/// JNI type name, e.g., "android/app/Activity".
15-
/// Extracted from the [Register] attribute.
15+
/// Extracted from the [Register] attribute or auto-computed during scanning.
16+
/// Manifest rooting may later promote this to <see cref="CompatJniName"/> when
17+
/// a component is referenced by its managed-namespace form.
1618
/// </summary>
17-
public required string JavaName { get; init; }
19+
public required string JavaName { get; set; }
1820

1921
/// <summary>
2022
/// Compat JNI type name, e.g., "myapp.namespace/MyType" for user types (uses raw namespace, not CRC64).
@@ -48,7 +50,7 @@ public sealed record JavaPeerInfo
4850
/// that extends Activity. Null for java/lang/Object or types without a Java base.
4951
/// Needed by JCW Java source generation ("extends" clause).
5052
/// </summary>
51-
public string? BaseJavaName { get; init; }
53+
public string? BaseJavaName { get; set; }
5254

5355
/// <summary>
5456
/// JNI names of Java interfaces this type implements, e.g., ["android/view/View$OnClickListener"].
@@ -69,16 +71,23 @@ public sealed record JavaPeerInfo
6971
/// Types with component attributes ([Activity], [Service], etc.),
7072
/// custom views from layout XML, or manifest-declared components
7173
/// are unconditionally preserved (not trimmable).
74+
/// May be set to <c>true</c> after scanning when the manifest references a type
75+
/// that the scanner did not mark as unconditional. Should only ever be set
76+
/// to <c>true</c>, never back to <c>false</c>.
7277
/// </summary>
73-
public bool IsUnconditional { get; init; }
78+
public bool IsUnconditional { get; set; }
7479

7580
/// <summary>
76-
/// True for Application and Instrumentation types. These types cannot call
81+
/// True for Application and Instrumentation types, plus any generated managed
82+
/// base classes they rely on during startup. These types cannot call
7783
/// <c>registerNatives</c> in their static initializer because the native library
7884
/// (<c>libmonodroid.so</c>) is not loaded until after the Application class is instantiated.
7985
/// Registration is deferred to <c>ApplicationRegistration.registerApplications()</c>.
86+
/// This may also be set after scanning when a type is only discovered from
87+
/// manifest <c>android:name</c> usage on <c>&lt;application&gt;</c> or
88+
/// <c>&lt;instrumentation&gt;</c>.
8089
/// </summary>
81-
public bool CannotRegisterInStaticConstructor { get; init; }
90+
public bool CannotRegisterInStaticConstructor { get; set; }
8291

8392
/// <summary>
8493
/// Marshal methods: methods with [Register(name, sig, connector)], [Export], or

0 commit comments

Comments
 (0)