diff --git a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs index a4ab68225b0..271b8a73c8d 100644 --- a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs +++ b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs @@ -15,6 +15,8 @@ using Java.Security; using Javax.Net.Ssl; +using Microsoft.Android.Runtime; + namespace Android.Runtime { public static class AndroidEnvironment { @@ -26,6 +28,7 @@ public static class AndroidEnvironment { static object lock_ = new object (); [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] static Type? httpMessageHandlerType; + static bool isFirstGetHttpMessageHandlerCall = true; static void SetupTrustManager () { @@ -333,66 +336,91 @@ static IWebProxy GetDefaultProxy () // This is invoked by // System.Net.Http.dll!System.Net.Http.HttpClient.cctor // DO NOT REMOVE - [DynamicDependency (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof (Xamarin.Android.Net.AndroidMessageHandler))] static object GetHttpMessageHandler () { - [UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Preserved by the MarkJavaObjects trimmer step.")] - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - static Type TypeGetType (string typeName) => - Type.GetType (typeName, throwOnError: false); - - if (httpMessageHandlerType is null) { - var handlerTypeName = Environment.GetEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE")?.Trim (); - Type? handlerType = null; - if (!String.IsNullOrEmpty (handlerTypeName)) - handlerType = TypeGetType (handlerTypeName); - - // We don't do any type checking or casting here to avoid dependency on System.Net.Http in Mono.Android.dll - if (handlerType is null || !IsAcceptableHttpMessageHandlerType (handlerType)) { - handlerType = GetFallbackHttpMessageHandlerType (); + lock (lock_) { + if (isFirstGetHttpMessageHandlerCall) { + isFirstGetHttpMessageHandlerCall = false; + + string? handlerTypeName = Environment.GetEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE")?.Trim (); + if (RuntimeFeature.XaHttpClientHandlerTypeEnvironmentVariable) { + if (!string.IsNullOrEmpty (handlerTypeName)) { + Logger.Log (LogLevel.Warn, AndroidLogAppName, $"The $(AndroidHttpClientHandlerType) MSBuild property and the XA_HTTP_CLIENT_HANDLER_TYPE environment variable have been deprecated. If you need to use a custom HTTP handler, consider pass it to the HttpClient via constructor."); + } + + httpMessageHandlerType = GetHttpMessageHandlerType (handlerTypeName); + } else { + if (!string.IsNullOrEmpty (handlerTypeName)) { + Logger.Log (LogLevel.Warn, AndroidLogAppName, $"The $(AndroidHttpClientHandlerType) MSBuild property and the XA_HTTP_CLIENT_HANDLER_TYPE environment variable have been deprecated and they will be ignored (value: '{handlerTypeName}'). The default {typeof(Xamarin.Android.Net.AndroidMessageHandler).FullName} handler will be used. If you need to use a custom HTTP handler, pass it to the HttpClient via the constructor."); + Logger.Log (LogLevel.Warn, AndroidLogAppName, $"If your codebase relies on the legacy behavior, you can reenable the old behavior by setting the $(AndroidEnableLegacyXaHttpClientHandlerTypeEnvironmentVariable) MSBuild property to 'true' in your project file."); + } + } } + } + + if (RuntimeFeature.XaHttpClientHandlerTypeEnvironmentVariable) { + System.Diagnostics.Debug.Assert(httpMessageHandlerType is not null); - httpMessageHandlerType = handlerType; + return Activator.CreateInstance (httpMessageHandlerType) + ?? throw new InvalidOperationException ($"Could not create an instance of HTTP message handler type {httpMessageHandlerType.AssemblyQualifiedName}"); } - return Activator.CreateInstance (httpMessageHandlerType) - ?? throw new InvalidOperationException ($"Could not create an instance of HTTP message handler type {httpMessageHandlerType.AssemblyQualifiedName}"); + return new Xamarin.Android.Net.AndroidMessageHandler (); } - static bool IsAcceptableHttpMessageHandlerType (Type handlerType) + [RequiresUnreferencedCode ("The handler type might be removed by the trimmer.")] + private static Type GetHttpMessageHandlerType (string? handlerTypeName) { - if (Extends (handlerType, "System.Net.Http.HttpClientHandler, System.Net.Http")) { - // It's not possible to construct HttpClientHandler in this method because it would cause infinite recursion - // as HttpClientHandler's constructor calls the GetHttpMessageHandler function - Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} cannot be used as the native HTTP handler because it is derived from System.Net.Htt.HttpClientHandler. Use a type that extends System.Net.Http.HttpMessageHandler instead."); - return false; + Type? handlerType = null; + if (!String.IsNullOrEmpty (handlerTypeName)) { + handlerType = Type.GetType (handlerTypeName); + + if (handlerType is null) { + Logger.Log (LogLevel.Warn, AndroidLogAppName, $"The type {handlerTypeName} set as the default HTTP handler was not found. The type was probably linked away."); + } } - if (!Extends (handlerType, "System.Net.Http.HttpMessageHandler, System.Net.Http")) { - Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} set as the default HTTP handler is invalid. Use a type that extends System.Net.Http.HttpMessageHandler."); - return false; + + if (handlerType is null || !IsAcceptableHttpMessageHandlerType (handlerType)) { + handlerType = GetFallbackHttpMessageHandlerType (); } - return true; - } + return handlerType; - static bool Extends ( - Type handlerType, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - string baseTypeName) - { - var baseType = Type.GetType (baseTypeName, throwOnError: false); - return baseType?.IsAssignableFrom (handlerType) ?? false; - } + static bool IsAcceptableHttpMessageHandlerType (Type handlerType) + { + if (Extends (handlerType, "System.Net.Http.HttpClientHandler, System.Net.Http")) { + // It's not possible to construct HttpClientHandler in this method because it would cause infinite recursion + // as HttpClientHandler's constructor calls the GetHttpMessageHandler function + Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} cannot be used as the native HTTP handler because it is derived from System.Net.Htt.HttpClientHandler. Use a type that extends System.Net.Http.HttpMessageHandler instead."); + return false; + } + if (!Extends (handlerType, "System.Net.Http.HttpMessageHandler, System.Net.Http")) { + Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} set as the default HTTP handler is invalid. Use a type that extends System.Net.Http.HttpMessageHandler."); + return false; + } - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - static Type GetFallbackHttpMessageHandlerType () - { - const string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android"; - var handlerType = Type.GetType (typeName, throwOnError: false) - ?? throw new InvalidOperationException ($"The {typeName} was not found. The type was probably linked away."); + return true; + } - Logger.Log (LogLevel.Info, "MonoAndroid", $"Using {typeName} as the native HTTP message handler."); - return handlerType; + static bool Extends ( + Type handlerType, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + string baseTypeName) + { + var baseType = Type.GetType (baseTypeName, throwOnError: false); + return baseType?.IsAssignableFrom (handlerType) ?? false; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static Type GetFallbackHttpMessageHandlerType () + { + const string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android"; + var handlerType = Type.GetType (typeName, throwOnError: false) + ?? throw new InvalidOperationException ($"The {typeName} was not found. The type was probably linked away."); + + Logger.Log (LogLevel.Info, "MonoAndroid", $"Using {typeName} as the native HTTP message handler."); + return handlerType; + } } class _Proxy : IWebProxy { diff --git a/src/Mono.Android/ILLink/ILLink.Substitutions.xml b/src/Mono.Android/ILLink/ILLink.Substitutions.xml index 239252fe937..9ebb1d8784c 100644 --- a/src/Mono.Android/ILLink/ILLink.Substitutions.xml +++ b/src/Mono.Android/ILLink/ILLink.Substitutions.xml @@ -8,5 +8,9 @@ + + + + diff --git a/src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs b/src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs index 814b8c5ab7e..32b7a2ff93d 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs @@ -10,4 +10,9 @@ static class RuntimeFeature [FeatureSwitchDefinition ($"{FeatureSwitchPrefix}{nameof (ManagedTypeMap)}")] internal static bool ManagedTypeMap { get; } = AppContext.TryGetSwitch ($"{FeatureSwitchPrefix}{nameof (ManagedTypeMap)}", out bool isEnabled) ? isEnabled : false; + + [FeatureSwitchDefinition ($"{FeatureSwitchPrefix}{nameof (XaHttpClientHandlerTypeEnvironmentVariable)}")] + [FeatureGuard (typeof (RequiresUnreferencedCodeAttribute))] + internal static bool XaHttpClientHandlerTypeEnvironmentVariable { get; } = + AppContext.TryGetSwitch ($"{FeatureSwitchPrefix}{nameof (XaHttpClientHandlerTypeEnvironmentVariable)}", out bool isEnabled) ? isEnabled : false; } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets index d2053ce9e21..6e9f076b883 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets @@ -51,6 +51,10 @@ See: https://github.com/dotnet/runtime/blob/b13715b6984889a709ba29ea8a1961db469f Value="$([MSBuild]::ValueOrDefault('$(_AndroidUseManagedTypeMap)', 'false'))" Trim="true" /> + 0 ? typeName.Substring(0, indexOfComma) : typeName; - Assert.AreEqual (expectedTypeName, handler.GetType ().FullName); + Assert.AreEqual ("Xamarin.Android.Net.AndroidMessageHandler", handler.GetType ().FullName); } private static object? GetHttpMessageHandler (string? typeName) @@ -80,6 +55,8 @@ private static void ClearHttpMessageHandlerTypeCache () { var cacheField = typeof (AndroidEnvironment).GetField ("httpMessageHandlerType", BindingFlags.Static | BindingFlags.NonPublic)!; cacheField.SetValue (null, null); + var isFirstGetHttpMessageHandlerCallField = typeof (AndroidEnvironment).GetField ("isFirstGetHttpMessageHandlerCall", BindingFlags.Static | BindingFlags.NonPublic)!; + isFirstGetHttpMessageHandlerCallField.SetValue (null, false); } } }