diff --git a/eng/Versions.props b/eng/Versions.props
index d87b6a2fe34..bf1e2b58feb 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -16,6 +16,7 @@
11.0.100-preview.1.26076.102
0.11.5-preview.26076.102
9.0.4
+ 11.0.0-preview.1.26104.118
36.1.30
$(MicrosoftNETSdkAndroidManifest100100PackageVersion)
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/CompilerFeaturePolyfills.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/CompilerFeaturePolyfills.cs
new file mode 100644
index 00000000000..6b5fc126876
--- /dev/null
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/CompilerFeaturePolyfills.cs
@@ -0,0 +1,28 @@
+// Polyfills for C# language features on netstandard2.0
+
+// Required for init-only setters
+namespace System.Runtime.CompilerServices
+{
+ static class IsExternalInit { }
+
+ [AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
+ sealed class RequiredMemberAttribute : Attribute { }
+
+ [AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+ sealed class CompilerFeatureRequiredAttribute : Attribute
+ {
+ public CompilerFeatureRequiredAttribute (string featureName)
+ {
+ FeatureName = featureName;
+ }
+
+ public string FeatureName { get; }
+ public bool IsOptional { get; init; }
+ }
+}
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ [AttributeUsage (AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
+ sealed class SetsRequiredMembersAttribute : Attribute { }
+}
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj
new file mode 100644
index 00000000000..a8ba3b210e7
--- /dev/null
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+ $(TargetFrameworkNETStandard)
+ enable
+ Nullable
+ Microsoft.Android.Sdk.TrimmableTypeMap
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/CustomAttributeTypeProvider.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/CustomAttributeTypeProvider.cs
new file mode 100644
index 00000000000..0d12aa176fb
--- /dev/null
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/CustomAttributeTypeProvider.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection.Metadata;
+
+namespace Microsoft.Android.Sdk.TrimmableTypeMap;
+
+///
+/// Minimal ICustomAttributeTypeProvider implementation for decoding
+/// custom attribute values via System.Reflection.Metadata.
+///
+sealed class CustomAttributeTypeProvider : ICustomAttributeTypeProvider
+{
+ readonly MetadataReader reader;
+ Dictionary? enumTypeCache;
+
+ public CustomAttributeTypeProvider (MetadataReader reader)
+ {
+ this.reader = reader;
+ }
+
+ public string GetPrimitiveType (PrimitiveTypeCode typeCode) => typeCode.ToString ();
+
+ public string GetTypeFromDefinition (MetadataReader metadataReader, TypeDefinitionHandle handle, byte rawTypeKind)
+ {
+ var typeDef = metadataReader.GetTypeDefinition (handle);
+ var name = metadataReader.GetString (typeDef.Name);
+ if (typeDef.IsNested) {
+ var parent = GetTypeFromDefinition (metadataReader, typeDef.GetDeclaringType (), rawTypeKind);
+ return $"{parent}+{name}";
+ }
+ var ns = metadataReader.GetString (typeDef.Namespace);
+ return ns.Length > 0 ? $"{ns}.{name}" : name;
+ }
+
+ public string GetTypeFromReference (MetadataReader metadataReader, TypeReferenceHandle handle, byte rawTypeKind)
+ {
+ var typeRef = metadataReader.GetTypeReference (handle);
+ var name = metadataReader.GetString (typeRef.Name);
+ if (typeRef.ResolutionScope.Kind == HandleKind.TypeReference) {
+ var parent = GetTypeFromReference (metadataReader, (TypeReferenceHandle)typeRef.ResolutionScope, rawTypeKind);
+ return $"{parent}+{name}";
+ }
+ var ns = metadataReader.GetString (typeRef.Namespace);
+ return ns.Length > 0 ? $"{ns}.{name}" : name;
+ }
+
+ public string GetTypeFromSerializedName (string name) => name;
+
+ public PrimitiveTypeCode GetUnderlyingEnumType (string type)
+ {
+ if (enumTypeCache == null) {
+ enumTypeCache = BuildEnumTypeCache ();
+ }
+
+ if (enumTypeCache.TryGetValue (type, out var code)) {
+ return code;
+ }
+
+ // Default to Int32 for enums defined in other assemblies
+ return PrimitiveTypeCode.Int32;
+ }
+
+ Dictionary BuildEnumTypeCache ()
+ {
+ var cache = new Dictionary ();
+
+ foreach (var typeHandle in reader.TypeDefinitions) {
+ var typeDef = reader.GetTypeDefinition (typeHandle);
+
+ // Only process enum types
+ if (!IsEnum (typeDef))
+ continue;
+
+ var fullName = GetTypeFromDefinition (reader, typeHandle, rawTypeKind: 0);
+ var code = GetEnumUnderlyingTypeCode (typeDef);
+ cache [fullName] = code;
+ }
+
+ return cache;
+ }
+
+ bool IsEnum (TypeDefinition typeDef)
+ {
+ var baseType = typeDef.BaseType;
+ if (baseType.IsNil)
+ return false;
+
+ string? baseFullName = baseType.Kind switch {
+ HandleKind.TypeReference => GetTypeFromReference (reader, (TypeReferenceHandle)baseType, rawTypeKind: 0),
+ HandleKind.TypeDefinition => GetTypeFromDefinition (reader, (TypeDefinitionHandle)baseType, rawTypeKind: 0),
+ _ => null,
+ };
+
+ return baseFullName == "System.Enum";
+ }
+
+ PrimitiveTypeCode GetEnumUnderlyingTypeCode (TypeDefinition typeDef)
+ {
+ // For enums, the first instance field is the underlying value__ field
+ foreach (var fieldHandle in typeDef.GetFields ()) {
+ var field = reader.GetFieldDefinition (fieldHandle);
+ if ((field.Attributes & System.Reflection.FieldAttributes.Static) != 0)
+ continue;
+
+ var sig = field.DecodeSignature (SignatureTypeProvider.Instance, genericContext: null);
+ return sig switch {
+ "System.Byte" => PrimitiveTypeCode.Byte,
+ "System.SByte" => PrimitiveTypeCode.SByte,
+ "System.Int16" => PrimitiveTypeCode.Int16,
+ "System.UInt16" => PrimitiveTypeCode.UInt16,
+ "System.Int32" => PrimitiveTypeCode.Int32,
+ "System.UInt32" => PrimitiveTypeCode.UInt32,
+ "System.Int64" => PrimitiveTypeCode.Int64,
+ "System.UInt64" => PrimitiveTypeCode.UInt64,
+ _ => PrimitiveTypeCode.Int32,
+ };
+ }
+
+ return PrimitiveTypeCode.Int32;
+ }
+
+ public string GetSystemType () => "System.Type";
+
+ public string GetSZArrayType (string elementType) => $"{elementType}[]";
+
+ public bool IsSystemType (string type) => type == "System.Type" || type == "Type";
+}
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs
new file mode 100644
index 00000000000..85472f1b3ba
--- /dev/null
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Android.Sdk.TrimmableTypeMap;
+
+///
+/// Represents a Java peer type discovered during assembly scanning.
+/// Contains all data needed by downstream generators (TypeMap IL, UCO wrappers, JCW Java sources).
+/// Generators consume this data model — they never touch PEReader/MetadataReader.
+///
+sealed record JavaPeerInfo
+{
+ ///
+ /// JNI type name, e.g., "android/app/Activity".
+ /// Extracted from the [Register] attribute.
+ ///
+ public required string JavaName { get; init; }
+
+ ///
+ /// Compat JNI type name, e.g., "myapp.namespace/MyType" for user types (uses raw namespace, not CRC64).
+ /// For MCW binding types (with [Register]), this equals .
+ /// Used by acw-map.txt to support legacy custom view name resolution in layout XMLs.
+ ///
+ public required string CompatJniName { get; init; }
+
+ ///
+ /// Full managed type name, e.g., "Android.App.Activity".
+ ///
+ public required string ManagedTypeName { get; init; }
+
+ ///
+ /// Assembly name the type belongs to, e.g., "Mono.Android".
+ ///
+ public required string AssemblyName { get; init; }
+
+ ///
+ /// JNI name of the base Java type, e.g., "android/app/Activity" for a type
+ /// that extends Activity. Null for java/lang/Object or types without a Java base.
+ /// Needed by JCW Java source generation ("extends" clause).
+ ///
+ public string? BaseJavaName { get; init; }
+
+ ///
+ /// JNI names of Java interfaces this type implements, e.g., ["android/view/View$OnClickListener"].
+ /// Needed by JCW Java source generation ("implements" clause).
+ ///
+ public IReadOnlyList ImplementedInterfaceJavaNames { get; init; } = Array.Empty ();
+
+ public bool IsInterface { get; init; }
+ public bool IsAbstract { get; init; }
+
+ ///
+ /// If true, this is a Managed Callable Wrapper (MCW) binding type.
+ /// No JCW or RegisterNatives will be generated for it.
+ ///
+ public bool DoNotGenerateAcw { get; init; }
+
+ ///
+ /// Types with component attributes ([Activity], [Service], etc.),
+ /// custom views from layout XML, or manifest-declared components
+ /// are unconditionally preserved (not trimmable).
+ ///
+ public bool IsUnconditional { get; init; }
+
+ ///
+ /// Marshal methods: methods with [Register(name, sig, connector)], [Export], or
+ /// constructor registrations ([Register(".ctor", sig, "")] / [JniConstructorSignature]).
+ /// Constructors are identified by .
+ /// Ordered — the index in this list is the method's ordinal for RegisterNatives.
+ ///
+ public IReadOnlyList MarshalMethods { get; init; } = Array.Empty ();
+
+ ///
+ /// Information about the activation constructor for this type.
+ /// May reference a base type's constructor if the type doesn't define its own.
+ ///
+ public ActivationCtorInfo? ActivationCtor { get; init; }
+
+ ///
+ /// For interfaces and abstract types, the name of the invoker type
+ /// used to instantiate instances from Java.
+ ///
+ public string? InvokerTypeName { get; init; }
+
+ ///
+ /// True if this is an open generic type definition.
+ /// Generic types get TypeMap entries but CreateInstance throws NotSupportedException.
+ ///
+ public bool IsGenericDefinition { get; init; }
+}
+
+///
+/// Describes a marshal method (a method with [Register] or [Export]) on a Java peer type.
+/// Contains all data needed to generate a UCO wrapper, a JCW native declaration,
+/// and a RegisterNatives call.
+///
+sealed record MarshalMethodInfo
+{
+ ///
+ /// JNI method name, e.g., "onCreate".
+ /// This is the Java method name (without n_ prefix).
+ ///
+ public required string JniName { get; init; }
+
+ ///
+ /// JNI method signature, e.g., "(Landroid/os/Bundle;)V".
+ /// Contains both parameter types and return type.
+ ///
+ public required string JniSignature { get; init; }
+
+ ///
+ /// The connector string from [Register], e.g., "GetOnCreate_Landroid_os_Bundle_Handler".
+ /// Null for [Export] methods.
+ ///
+ public string? Connector { get; init; }
+
+ ///
+ /// Name of the managed method this maps to, e.g., "OnCreate".
+ ///
+ public required string ManagedMethodName { get; init; }
+
+ ///
+ /// True if this is a constructor registration.
+ ///
+ public bool IsConstructor { get; init; }
+
+ ///
+ /// For [Export] methods: Java exception types that the method declares it can throw.
+ /// Null for [Register] methods.
+ ///
+ public IReadOnlyList? ThrownNames { get; init; }
+
+ ///
+ /// For [Export] methods: super constructor arguments string.
+ /// Null for [Register] methods.
+ ///
+ public string? SuperArgumentsString { get; init; }
+}
+
+///
+/// Describes how to call the activation constructor for a Java peer type.
+///
+sealed record ActivationCtorInfo
+{
+ ///
+ /// The type that declares the activation constructor.
+ /// May be the type itself or a base type.
+ ///
+ public required string DeclaringTypeName { get; init; }
+
+ ///
+ /// The assembly containing the declaring type.
+ ///
+ public required string DeclaringAssemblyName { get; init; }
+
+ ///
+ /// The style of activation constructor found.
+ ///
+ public required ActivationCtorStyle Style { get; init; }
+}
+
+enum ActivationCtorStyle
+{
+ ///
+ /// Xamarin.Android style: (IntPtr handle, JniHandleOwnership transfer)
+ ///
+ XamarinAndroid,
+
+ ///
+ /// Java.Interop style: (ref JniObjectReference reference, JniObjectReferenceOptions options)
+ ///
+ JavaInterop,
+}
diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/SignatureTypeProvider.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/SignatureTypeProvider.cs
new file mode 100644
index 00000000000..24ff6045787
--- /dev/null
+++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/SignatureTypeProvider.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Immutable;
+using System.Reflection.Metadata;
+
+namespace Microsoft.Android.Sdk.TrimmableTypeMap;
+
+///
+/// Minimal ISignatureTypeProvider implementation for decoding method
+/// signatures via System.Reflection.Metadata.
+/// Returns fully qualified type name strings.
+///
+sealed class SignatureTypeProvider : ISignatureTypeProvider
+{
+ public static readonly SignatureTypeProvider Instance = new ();
+
+ public string GetPrimitiveType (PrimitiveTypeCode typeCode) => typeCode switch {
+ PrimitiveTypeCode.Void => "System.Void",
+ PrimitiveTypeCode.Boolean => "System.Boolean",
+ PrimitiveTypeCode.Char => "System.Char",
+ PrimitiveTypeCode.SByte => "System.SByte",
+ PrimitiveTypeCode.Byte => "System.Byte",
+ PrimitiveTypeCode.Int16 => "System.Int16",
+ PrimitiveTypeCode.UInt16 => "System.UInt16",
+ PrimitiveTypeCode.Int32 => "System.Int32",
+ PrimitiveTypeCode.UInt32 => "System.UInt32",
+ PrimitiveTypeCode.Int64 => "System.Int64",
+ PrimitiveTypeCode.UInt64 => "System.UInt64",
+ PrimitiveTypeCode.Single => "System.Single",
+ PrimitiveTypeCode.Double => "System.Double",
+ PrimitiveTypeCode.String => "System.String",
+ PrimitiveTypeCode.Object => "System.Object",
+ PrimitiveTypeCode.IntPtr => "System.IntPtr",
+ PrimitiveTypeCode.UIntPtr => "System.UIntPtr",
+ PrimitiveTypeCode.TypedReference => "System.TypedReference",
+ _ => typeCode.ToString (),
+ };
+
+ public string GetTypeFromDefinition (MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
+ {
+ var typeDef = reader.GetTypeDefinition (handle);
+ var ns = reader.GetString (typeDef.Namespace);
+ var name = reader.GetString (typeDef.Name);
+ if (typeDef.IsNested) {
+ var parent = GetTypeFromDefinition (reader, typeDef.GetDeclaringType (), rawTypeKind);
+ return $"{parent}+{name}";
+ }
+ return ns.Length > 0 ? $"{ns}.{name}" : name;
+ }
+
+ public string GetTypeFromReference (MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
+ {
+ var typeRef = reader.GetTypeReference (handle);
+ var name = reader.GetString (typeRef.Name);
+
+ // Handle nested types: if the ResolutionScope is another TypeReference, resolve recursively
+ if (typeRef.ResolutionScope.Kind == HandleKind.TypeReference) {
+ var parent = GetTypeFromReference (reader, (TypeReferenceHandle)typeRef.ResolutionScope, rawTypeKind);
+ return $"{parent}+{name}";
+ }
+
+ var ns = reader.GetString (typeRef.Namespace);
+ return ns.Length > 0 ? $"{ns}.{name}" : name;
+ }
+
+ public string GetTypeFromSpecification (MetadataReader reader, object? genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
+ {
+ var typeSpec = reader.GetTypeSpecification (handle);
+ return typeSpec.DecodeSignature (this, genericContext);
+ }
+
+ public string GetSZArrayType (string elementType) => $"{elementType}[]";
+ public string GetArrayType (string elementType, ArrayShape shape) => $"{elementType}[{new string (',', shape.Rank - 1)}]";
+ public string GetByReferenceType (string elementType) => $"{elementType}&";
+ public string GetPointerType (string elementType) => $"{elementType}*";
+ public string GetPinnedType (string elementType) => elementType;
+ public string GetModifiedType (string modifier, string unmodifiedType, bool isRequired) => unmodifiedType;
+
+ public string GetGenericInstantiation (string genericType, ImmutableArray typeArguments)
+ {
+ return $"{genericType}<{string.Join (",", typeArguments)}>";
+ }
+
+ public string GetGenericTypeParameter (object? genericContext, int index) => $"!{index}";
+ public string GetGenericMethodParameter (object? genericContext, int index) => $"!!{index}";
+
+ public string GetFunctionPointerType (MethodSignature signature) => "delegate*";
+}