Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When using TrimMode=link custom AndroidHttpClientHandlerType is not preserved #8797

Open
jonathanpeppers opened this issue Mar 7, 2024 · 1 comment · May be fixed by #8954
Open

When using TrimMode=link custom AndroidHttpClientHandlerType is not preserved #8797

jonathanpeppers opened this issue Mar 7, 2024 · 1 comment · May be fixed by #8954
Assignees
Labels
Area: App+Library Build Issues when building Library projects or Application projects. bug Component does not function as intended.
Milestone

Comments

@jonathanpeppers
Copy link
Member

Android application type

.NET Android (net7.0-android, net8.0-android, etc.)

Affected platform version

.NET 8, 9, etc.

Description

If you use the project settings:

<TrimMode>link</TrimMode>
<AndroidHttpClientHandlerType>MyNamespace.MyCustomHandler</AndroidHttpClientHandlerType>

In this case, nothing would preserve MyCustomHandler and it would be trimmed away.

Steps to Reproduce

Use project settings above.

Did you find any workaround?

You could use either [DynamicDependency] or [DynamicallyAccessedMembers] to preserve the type. You could also instantiate the handler in code.

Relevant log output

No response

@jonathanpeppers jonathanpeppers added bug Component does not function as intended. Area: App+Library Build Issues when building Library projects or Application projects. labels Mar 7, 2024
@jonathanpeppers jonathanpeppers added this to the .NET 9 milestone Mar 7, 2024
@jonathanpeppers jonathanpeppers self-assigned this Mar 7, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the needs-triage Issues that need to be assigned. label Mar 7, 2024
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Mar 7, 2024
Fixes: xamarin#5652

Each trimming problem listed below.

~~ AndroidEnvironment ~~

    src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(373,19): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
    src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(379,22): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
    src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(342,20): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
    src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(352,11): error IL2077: 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The field 'Android.Runtime.AndroidEnvironment.httpMessageHandlerType' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

To fix these:

* Use constant strings for calls like:

    Type.GetType ("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false)

Use `Type.IsAssignableFrom()` inline, and the trimmer now understands
the code paths.

There is a problem here if either of these are used with a custom type:

* `$(AndroidHttpClientHandlerType)`
* `$XA_HTTP_CLIENT_HANDLER_TYPE`

This is a rarely used feature, I found one usage in GitHub:

https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code

Filed an issue to fix this in the future:

* xamarin#8797

~~ JavaConvert ~~

    src\Mono.Android\Java.Interop\JavaConvert.cs(223,12): error IL2091: 'TResult' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaObjectExtensions._JavaCast<TResult>(IJavaObject)'. The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject, out Boolean)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
    src\Mono.Android\Java.Interop\JavaConvert.cs(254,12): error IL2067: 'resultType' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'Java.Interop.JavaObjectExtensions.JavaCast(IJavaObject, Type)'. The parameter 'targetType' of method 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject, Type)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
    src\Mono.Android\Java.Interop\JavaConvert.cs(67,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
    src\Mono.Android\Java.Interop\JavaConvert.cs(73,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
    src\Mono.Android\Java.Interop\JavaConvert.cs(79,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

Adding attributes fixed some of these.

I suppressed warnings for:

* `Type.MakeGenericType()`: `JavaDictionary<,>`, `JavaList<>`, and
  `JavaCollection<>` use DynamicDependency to preserve `FromJniHandle`
  method.

* `Type.GetElementType()`: for calling `MyJavaObject[]` constructors,
  the `MarkJavaObjects` trimmer step should preserve these
  constructors.

This trickled over to require more attributes for:

* `AdapterView`
* `ArrayAdapter`
* `AsyncTask`
* `JavaCollection`
* `JavaDictionary`
* `JavaList`
* `JavaList`
* `JavaObjectExtensions`
* `JavaSet`
* `SparseArray`
* `System.Linq\Extensions`

~~ JNIEnv ~~

    src\Mono.Android\Android.Runtime\JNIEnv.cs(810,38): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(953,33): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(1078,44): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(1139,15): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(Object[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(1060,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(1065,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
    src\Mono.Android\Android.Runtime\JNIEnv.cs(1257,23): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJniHandle<T>(nint, JniHandleOwnership)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(nint, T[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

Adding some attributes fixed two of these.

Suppress NativeAOT warnings, in source, for:

* `Array.CreateInstance()`

* `Type.MakeArrayType()`

Link to an issue to fix in the future.
@jpobst jpobst removed the needs-triage Issues that need to be assigned. label Mar 7, 2024
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Mar 12, 2024
commit 79b9908
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Tue Mar 12 11:51:23 2024 -0500

    Update justification message

commit db695b4
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Mon Mar 11 15:04:10 2024 -0500

    [DAM] not needed on JavaCollection<T>, JavaList<T>, JavaDictionary<K,V>

commit ffd33ed
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Mon Mar 11 15:02:09 2024 -0500

    One less IL2068 suppression

commit 3e34f0f
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Mon Mar 11 09:02:33 2024 -0500

    Set `$(IlcTreatWarningsAsErrors)`

commit e4bbe8c
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Mon Mar 11 08:43:49 2024 -0500

    Changes to make diff in `AndroidEnvironment` smaller

commit 6399051
Author: Jonathan Peppers <jonathan.peppers@microsoft.com>
Date:   Thu Feb 29 12:33:10 2024 -0600

    [Mono.Android] is now "trimming safe"

    Fixes: xamarin#5652

    Each trimming problem listed below.

    ~~ AndroidEnvironment ~~

        src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(373,19): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
        src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(379,22): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
        src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(342,20): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
        src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(352,11): error IL2077: 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The field 'Android.Runtime.AndroidEnvironment.httpMessageHandlerType' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

    To fix these:

    * Use constant strings for calls like:

        Type.GetType ("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false)

    Use `Type.IsAssignableFrom()` inline, and the trimmer now understands
    the code paths.

    There is a problem here if either of these are used with a custom type:

    * `$(AndroidHttpClientHandlerType)`
    * `$XA_HTTP_CLIENT_HANDLER_TYPE`

    This is a rarely used feature, I found one usage in GitHub:

    https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code

    Filed an issue to fix this in the future:

    * xamarin#8797

    ~~ JavaConvert ~~

        src\Mono.Android\Java.Interop\JavaConvert.cs(223,12): error IL2091: 'TResult' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaObjectExtensions._JavaCast<TResult>(IJavaObject)'. The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject, out Boolean)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
        src\Mono.Android\Java.Interop\JavaConvert.cs(254,12): error IL2067: 'resultType' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'Java.Interop.JavaObjectExtensions.JavaCast(IJavaObject, Type)'. The parameter 'targetType' of method 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject, Type)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
        src\Mono.Android\Java.Interop\JavaConvert.cs(67,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
        src\Mono.Android\Java.Interop\JavaConvert.cs(73,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
        src\Mono.Android\Java.Interop\JavaConvert.cs(79,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

    Adding attributes fixed some of these.

    I suppressed warnings for:

    * `Type.MakeGenericType()`: `JavaDictionary<,>`, `JavaList<>`, and
      `JavaCollection<>` use DynamicDependency to preserve `FromJniHandle`
      method.

    * `Type.GetElementType()`: for calling `MyJavaObject[]` constructors,
      the `MarkJavaObjects` trimmer step should preserve these
      constructors.

    This trickled over to require more attributes for:

    * `AdapterView`
    * `ArrayAdapter`
    * `AsyncTask`
    * `JavaCollection`
    * `JavaDictionary`
    * `JavaList`
    * `JavaList`
    * `JavaObjectExtensions`
    * `JavaSet`
    * `SparseArray`
    * `System.Linq\Extensions`

    ~~ JNIEnv ~~

        src\Mono.Android\Android.Runtime\JNIEnv.cs(810,38): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(953,33): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(1078,44): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(1139,15): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(Object[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(1060,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(1065,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
        src\Mono.Android\Android.Runtime\JNIEnv.cs(1257,23): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJniHandle<T>(nint, JniHandleOwnership)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(nint, T[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

    Adding some attributes fixed two of these.

    Suppress NativeAOT warnings, in source, for:

    * `Array.CreateInstance()`

    * `Type.MakeArrayType()`

    Link to an issue to fix in the future.
jonpryor pushed a commit that referenced this issue Mar 13, 2024
Fixes: #5652

Add `build-tools/trim-analyzers/trim-analyzers.props`, which when
`<Import/>`ed into a project will enable trimmer analyzers and enable
`$(WarningsAsErrors)` for the warnings listed below so that that these
trimmer warnings need to be fixed as part of the build:

  * IL2000 through IL2129
  * IL3050 through IL3056

Update `Mono.Android.csproj` to import `trim-analyzers.props`.

Address the trimmer warnings that are now errors


~~ `$(AndroidHttpClientHandlerType)` and AndroidEnvironment ~~

The default `HttpClientHandler` type used with `new HttpClient()` can
be controlled via the [`$(AndroidHttpClientHandlerType)`][0] MSBuild
property, which sets the `$XA_HTTP_CLIENT_HANDLER_TYPE` environment
variable, which is used with `Type.GetType()`.  (Setting
`$(AndroidHttpClientHandlerType)` [is rare][1], has not been
deprecated, and thus is supported.)

The trimmer only supports `Type.GetType()` with string constants, not
with runtime values such as environment variables.

This results in numerous trimmer warnings:

	src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(373,19): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
	src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(379,22): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
	src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(342,20): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type.
	src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(352,11): error IL2077: 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'.
	  The field 'Android.Runtime.AndroidEnvironment.httpMessageHandlerType' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

Partially address these issues by:

  * Using string constants instead of default parameter values.

        // This works
        static Type GetFallbackHttpMessageHandlerType() {
            const string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android";
            Type.GetType(typeName, …);
        }

        // This does not, as the *caller* gets the value
        static Type GetFallbackHttpMessageHandlerType(string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android") {
            Type.GetType(typeName, …);
        }

  * Spread `[DynamicallyAccessedMembers]` around to fix IL2077.

  * Use `[UnconditionalSuppressMessage]` to explicitly suppress IL2057

TODO: we need to *actually preserve* the type referenced by
`$(AndroidHttpClientHandlerType)`.

  * #8797


~~ JavaConvert ~~

`JavaConvert` is used internally to marshal between Java and managed
types, and elicits numerous trimmer warnings:

	src\Mono.Android\Java.Interop\JavaConvert.cs(223,12): error IL2091: 'TResult' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	  'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaObjectExtensions._JavaCast<TResult>(IJavaObject)'.
	  The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject, out Boolean)' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.
	src\Mono.Android\Java.Interop\JavaConvert.cs(254,12): error IL2067: 'resultType' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	  'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'Java.Interop.JavaObjectExtensions.JavaCast(IJavaObject, Type)'.
	  The parameter 'targetType' of method 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject, Type)' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.
	src\Mono.Android\Java.Interop\JavaConvert.cs(67,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
	src\Mono.Android\Java.Interop\JavaConvert.cs(73,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.
	src\Mono.Android\Java.Interop\JavaConvert.cs(79,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

Address these by adding `[DynamicallyAccessedMembers]` to assist the
trimmer, `#pragma warning disable` to suppress the IL3050 warning,
and `[UnconditionalSuppressMessage]` to ignore the IL2055 warnings.

This trickled over to require more attributes for:

  * `AdapterView`
  * `ArrayAdapter`
  * `AsyncTask`
  * `JavaCollection`
  * `JavaDictionary`
  * `JavaList`
  * `JavaList`
  * `JavaObjectExtensions`
  * `JavaSet`
  * `SparseArray`
  * `System.Linq\Extensions`


~~ JNIEnv ~~

`JNIEnv` is also used internally to marshal between Java and managed
types, and elicits numerous trimmer warnings:

	src\Mono.Android\Android.Runtime\JNIEnv.cs(810,38): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(953,33): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(1078,44): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(1139,15): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	  'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJavaObject<T>(IJavaObject)'.
	  The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(Object[])' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(1060,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(1065,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available.
	src\Mono.Android\Android.Runtime\JNIEnv.cs(1257,23): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	  'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJniHandle<T>(nint, JniHandleOwnership)'.
	  The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(nint, T[])' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.

Adding `[DynamicallyAccessedMembers]` fixes the IL2091 warnings.

Use `#pragma warning disable` to ignore the IL3050 warnings around
`Array.CreateInstance()` and `Type.MakeArrayType()`.

TODO: try to fix these issues instead of suppressing them:

  * #8724

[0]: https://github.com/xamarin/xamarin-android/blob/c20d51fcf8e910b8fb46c5351c26e55ed1fab90c/Documentation/guides/building-apps/build-properties.md#androidhttpclienthandlertype
[1]: https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code
@jonpryor
Copy link
Member

Related: whatever approach is used to preserve $(AndroidHttpClientHandlerType) -- likely a linker XML file -- should also be used to preserve View subclasses which are used within Android Layout .axml files.

This is partially touched upon in #8805, which had to add [DynamicDependency(.All, typeof (Mono.Android_Test.Library.CustomTextView)) to ensure that the View subclass wasn't trimmed away. [DynamicDependency] should not be required, ideally.

jonpryor pushed a commit that referenced this issue Mar 20, 2024
Context: 5205a5f
Context: 8a5b2a0
Context: #8724
Context: #8797

As we have solved all trimming warnings (5205a5f. 8a5b2a0) in the
Android workload, we can now go "all in" on trimming.

Early in .NET 6 (maybe even 5?) we "hid" many trimming warnings as we
did not yet plan to solve them:

	<SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' ">true</SuppressTrimAnalysisWarnings>

These warnings were not *actionable* at the time for customers, as
many warnings were in `Mono.Android.dll`, `Java.Interop.dll`, etc.

Going forward, let's stop suppressing these warnings for
`$(TrimMode)`=full.

We can also enable trimming for new projects:

  * `dotnet new android`
  * `dotnet new android-wear`

New projects will have the [`$(TrimMode)`][0] property set to `Full`
by default:

	<!--
	  Enable full trimming in Release mode.
	  To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/trimming-options#trimming-granularity
	-->
	<PropertyGroup Condition="'$(Configuration)' == 'Release'">
	  <TrimMode>full</TrimMode>
	</PropertyGroup>

We wouldn't want to do this for existing projects *yet*, as they
might have existing code, NuGet packages, etc. where trimming
warnings might be present.

We can also improve the templates for Android class libraries:

  * `dotnet new androidlib`
  * `dotnet new android-bindinglib`

New class library projects will have the [`$(IsTrimmable)`][1]
property set to `true` by default:

	<!--
	  Enable trim analyzers for Android class libraries.
	  To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
	-->
	<IsTrimmable>true</IsTrimmable>

This way, new class libraries will be "trimmable" by default and be
able to react to trimming warnings.

We can also use `$(TrimMode)=full` in many of our existing tests:

  * MSBuild tests that assert 0 warnings can use `$(TrimMode)=full`.

  * On-device tests can use `$(TrimMode)=full`.


~~ General trimming warnings ~~

This was discovered through `Mono.Android-NET-Tests.csproj`, but
there were a few trimmer warnings in the "layout bindings" feature:

	…\dotnet\packs\Microsoft.Android.Sdk.Windows\…\tools\LayoutBinding.cs(79,56):
	  warning IL2091: Xamarin.Android.Design.LayoutBinding.<>c__DisplayClass8_0<T>.<FindFragment>b__0(Activity):
	  'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	    'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Android.App.FragmentManager.FindFragmentById<T>(Int32)'.
	  The generic parameter 'T' of 'Xamarin.Android.Design.LayoutBinding.<>c__DisplayClass8_0<T>' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.
	…\dotnet\packs\Microsoft.Android.Sdk.Windows\…\tools\LayoutBinding.cs(35,5):
	  warning IL2091: Xamarin.Android.Design.LayoutBinding.FindView<T>(Int32, T&):
	  'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	    'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Android.App.Activity.FindViewById<T>(Int32)'.
	  The generic parameter 'T' of 'Xamarin.Android.Design.LayoutBinding.FindView<T>(Int32, T&)' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.
	…\dotnet\packs\Microsoft.Android.Sdk.Windows\…\tools\LayoutBinding.cs(37,5):
	  warning IL2091: Xamarin.Android.Design.LayoutBinding.FindView<T>(Int32, T&):
	  'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors',
	    'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Android.Views.View.FindViewById<T>(Int32)'.
	  The generic parameter 'T' of 'Xamarin.Android.Design.LayoutBinding.FindView<T>(Int32, T&)' does not have matching annotations.
	  The source value must declare at least the same requirements as those declared on the target location it is assigned to.

We can `[DynamicallyAccessedMembers(Constructors)]` to fix these.


~~ Trimming warnings in tests ~~

Several tests that verify "trimming unsafe features" just specify:

	[RequiresUnreferencedCode ("Tests trimming unsafe features")]

If the test might have an issue under NativeAOT, I used:

	// FIXME: #8724
	#pragma warning disable IL3050

Places that use `Assembly.GetType()` can use `Type.GetType()` instead:

	-var JavaProxyThrowable_type = typeof (Java.Lang.Object)
	-    .Assembly
	-    .GetType ("Android.Runtime.JavaProxyThrowable");
	+var JavaProxyThrowable_type = Type.GetType ("Android.Runtime.JavaProxyThrowable, Mono.Android");

`SystemTests.AppDomainTest` was just ignored (and had warnings).
Update to just verify `PlatformNotSupportedException` is thrown.


~~ Test failures ~~

`JsonSerializerTest` requires setting
[`$(JsonSerializerIsReflectionEnabledByDefault)`][2]=true:

	<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>

Otherwise, an exception is thrown:

	System.InvalidOperationException : JsonSerializerIsReflectionDisabled

`Java.Interop-Tests` were initially not loaded at all, with the log
message:

	W NUnit   : Failed to load tests from assembly 'Java.Interop-Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065

If we make `Java.Interop-Tests.dll` a `@(TrimmerRootAssembly)`:

	<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="All" />

Then all the tests cases are preserved and can be run, in the same
way the "main app assembly" is preserved.

`Android.GraphicsTests.NinePatchTests` failed with:

	The drawable created from resource tile should be a NinePatchDrawable.
	Expected: not null
	But was:  null

The only usage of `NinePatchDrawable` was:

	Assert.IsNotNull (d as NinePatchDrawable);

`NinePatchDrawable` likely needs its interfaces and constructors
preserved for this test to pass.  I added an attribute for just `All`
members for the test to pass:

	[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (NinePatchDrawable))]

`Xamarin.Android.RuntimeTests.CustomWidgetTests` failed with:

	(Binary XML file line #1 in Mono.Android.NET_Tests:layout/uppercase_custom: Binary XML file line #1 in Mono.Android.NET_Tests:layout/uppercase_custom: Error inflating class Mono.Android_Test.Library.CustomTextView)
	   at Java.Interop.JniEnvironment.InstanceMethods.CallObjectMethod(JniObjectReference , JniMethodInfo , JniArgumentValue* )
	   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualObjectMethod(String , IJavaPeerable , JniArgumentValue* )
	   at Android.Views.LayoutInflater.Inflate(Int32 , ViewGroup )
	   at Xamarin.Android.RuntimeTests.CustomWidgetTests.<>c.<UpperCaseCustomWidget_ShouldNotThrowInflateException>b__0_0()
	   at NUnit.Framework.Constraints.VoidInvocationDescriptor.Invoke()
	   at NUnit.Framework.Constraints.ExceptionInterceptor.Intercept(Object )
	--- End of managed Java.Lang.RuntimeException stack trace ---
	android.view.InflateException: Binary XML file line #1 in Mono.Android.NET_Tests:layout/uppercase_custom: Binary XML file line #1 in Mono.Android.NET_Tests:layout/uppercase_custom: Error inflating class Mono.Android_Test.Library.CustomTextView
	Caused by: android.view.InflateException: Binary XML file line #1 in Mono.Android.NET_Tests:layout/uppercase_custom: Error inflating class Mono.Android_Test.Library.CustomTextView
	Caused by: java.lang.ClassNotFoundException: Mono.Android_Test.Library.CustomTextView
	   at java.lang.Class.classForName(Native Method)
	   at java.lang.Class.forName(Class.java:454)
	   at android.view.LayoutInflater.createView(LayoutInflater.java:815)
	   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
	   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
	   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
	   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
	   at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
	   at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
	   at android.view.LayoutInflater.inflate(LayoutInflater.java:481)
	   at crc643df67da7b13bb6b1.TestInstrumentation_1.n_onStart(Native Method)
	   at crc643df67da7b13bb6b1.TestInstrumentation_1.onStart(TestInstrumentation_1.java:32)
	   at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
	Caused by: java.lang.ClassNotFoundException: Didn't find class "Mono.Android_Test.Library.CustomTextView" on path

In this case, `Mono.Android_Test.Library.CustomTextView` was used
from an Android layout, but not used anywhere in managed code.

To fix, I added:

	[DynamicDependency (DynamicallyAccessedMemberTypes.All, typeof (Mono.Android_Test.Library.CustomTextView))]

I could have also made `Mono.Android_Test.Library` a `@(TrimmerRootAssembly)`.

TODO: `View` subclasses used within Android Layout `.axml` files
should be automatically preserved; see #8797.

[0]: https://learn.microsoft.com/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-8-0#trimming-granularity
[1]: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming?pivots=dotnet-8-0#enable-project-specific-trimming
[2]: https://learn.microsoft.com/dotnet/core/compatibility/serialization/8.0/publishtrimmed
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 14, 2024
Fixes: xamarin#8797
Context: https://github.com/dotnet/runtime/blob/714a4420805ed53c311b05381c83c88894100fa9/docs/tools/illink/data-formats.md
Context:

Here are two cases `TrimMode=full` can break applications:

* `AndroidHttpClientHandlerType` set to a custom type

* Custom views (Android `.xml`) that are not referenced in C# code

For either of these cases, the traditional approach in Xamarin.Android
would have been to author a trimmer step to preserve these types.

However, thinking about NativeAOT in the future, it is more
"future-proof" to write an MSBuild task that generates trimmer `.xml`
to preserve these types. This same XML file could be passed to the
NativeAOT trimmer (`Ilc`). If we had a custom trimmer step, we would
have to reimplement the same logic for the NativeAOT trimmer.

This is WIP as custom views are not yet preserved.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 16, 2024
Fixes: xamarin#8797
Context: https://github.com/dotnet/runtime/blob/714a4420805ed53c311b05381c83c88894100fa9/docs/tools/illink/data-formats.md
Context:

Here are two cases `TrimMode=full` can break applications:

* `AndroidHttpClientHandlerType` set to a custom type

* Custom views (Android `.xml`) that are not referenced in C# code

For either of these cases, the traditional approach in Xamarin.Android
would have been to author a trimmer step to preserve these types.

However, thinking about NativeAOT in the future, it is more
"future-proof" to write an MSBuild task that generates trimmer `.xml`
to preserve these types. This same XML file could be passed to the
NativeAOT trimmer (`Ilc`). If we had a custom trimmer step, we would
have to reimplement the same logic for the NativeAOT trimmer.

This is WIP as custom views are not yet preserved.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 16, 2024
Fixes: xamarin#8797

Here are two cases `TrimMode=full` can break applications:

* `$(AndroidHttpClientHandlerType)` set to a custom type

* Custom views (Android `.xml`) that are not referenced in C# code

In the `MarkJavaObjects` trimmer step we can preserve both of these
cases by:

* Passing in `$(AndroidHttpClientHandlerType)`, preserve the public,
  parameterless constructor of the type

* Pass in `$(_CustomViewMapFile)`, preserve `IJavaObject` types if
  they are found in the map file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: App+Library Build Issues when building Library projects or Application projects. bug Component does not function as intended.
Projects
None yet
3 participants