From f856a5738f01034ccce8e49ae0501a897caa496c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 12 May 2022 09:08:49 +0200 Subject: [PATCH 01/33] Add initial implementation of NT Authentication --- src/Mono.Android/Mono.Android.csproj | 2 + .../AndroidMessageHandler.cs | 15 ++ .../NTAuthenticationHelper.cs | 193 ++++++++++++++++++ .../NTAuthenticationProxy.cs | 86 ++++++++ 4 files changed, 296 insertions(+) create mode 100644 src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs create mode 100644 src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index cda6e0a5f5f..90608d45410 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -367,6 +367,8 @@ + + diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index fc446353310..57f417185ae 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -139,6 +139,8 @@ public CookieContainer CookieContainer public bool UseProxy { get; set; } = true; + private bool CouldHaveNTCredentials => Proxy != null || Credentials != null; + public IWebProxy? Proxy { get; set; } public ICredentials? Credentials { get; set; } @@ -332,6 +334,19 @@ string EncodeUrl (Uri url) /// Request provided by /// Cancellation token. protected override async Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); + +#if !MONOANDROID1_0 + if (CouldHaveNTCredentials && RequestNeedsAuthorization && NTAuthenticationHelper.TryGetSupportedAuthMethod (this, request, out var auth, out var credentials)) { + response = await NTAuthenticationHelper.SendAsync (this, request, response, auth, credentials, cancellationToken).ConfigureAwait (false); + } +#endif + + return response; + } + + internal async Task DoSendAsync (HttpRequestMessage request, CancellationToken cancellationToken) { AssertSelf (); if (request == null) diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs new file mode 100644 index 00000000000..4a1ed91bae0 --- /dev/null +++ b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Authentication.ExtendedProtection; +using System.Threading; +using System.Threading.Tasks; + +namespace Xamarin.Android.Net +{ + // This code is heavily inspired by System.Net.Http.AuthenticationHelper + internal static class NTAuthenticationHelper + { + const int MaxRequests = 10; + + internal static bool TryGetSupportedAuthMethod ( + AndroidMessageHandler handler, + HttpRequestMessage request, + [NotNullWhen (true)] out AuthenticationData? supportedAuth, + [NotNullWhen (true)] out NetworkCredential? suitableCredentials) + { + IEnumerable requestedAuthentication = handler.RequestedAuthentication ?? Enumerable.Empty (); + foreach (var auth in requestedAuthentication) { + if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { + var credentials = auth.UseProxyAuthentication ? handler.Proxy?.Credentials : handler.Credentials; + suitableCredentials = credentials?.GetCredential (request.RequestUri, authType); + + if (suitableCredentials != null) { + supportedAuth = auth; + return true; + } + } + } + + supportedAuth = null; + suitableCredentials = null; + return false; + } + + internal static async Task SendAsync ( + AndroidMessageHandler handler, + HttpRequestMessage request, + HttpResponseMessage response, + AuthenticationData auth, + NetworkCredential credentials, + CancellationToken cancellationToken) + { + var authType = GetSupportedAuthType (auth.Challenge); + var isProxyAuth = auth.UseProxyAuthentication; + var authContext = new NTAuthenticationProxy ( + isServer: false, + authType, + credentials, + spn: await GetSpn (handler, request, isProxyAuth, cancellationToken).ConfigureAwait (false), + requestedContextFlags: GetRequestedContextFlags (isProxyAuth), + channelBinding: null); + + // we need to make sure that the handler doesn't override the authorization header + // with the user defined pre-authentication data + var originalPreAuthenticate = handler.PreAuthenticate; + handler.PreAuthenticate = false; + + try { + string? challenge = null; + int requestCounter = 0; + + while (requestCounter++ < MaxRequests) { + var challengeResponse = authContext.GetOutgoingBlob (challenge); + if (challengeResponse == null) { + // response indicated denial even after login, so stop processing + // and return current response + break; + } + + var headerValue = new AuthenticationHeaderValue (authType, challengeResponse); + if (auth.UseProxyAuthentication) { + request.Headers.ProxyAuthorization = headerValue; + } else { + request.Headers.Authorization = headerValue; + } + + response = await handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); + + // we need to drain the content otherwise the next request + // won't reuse the same TCP socket and persistent auth won't work + await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); + + if (authContext.IsCompleted || !TryGetChallenge (response, authType, isProxyAuth, out challenge)) { + break; + } + + if (!IsAuthenticationChallenge (response, isProxyAuth)) { + // Tail response for Negotiate on successful authentication. + // Validate it before we proceed. + authContext.GetOutgoingBlob (challenge); + break; + } + } + + return response; + } finally { + handler.PreAuthenticate = originalPreAuthenticate; + authContext.CloseContext (); + } + } + + static string GetSupportedAuthType (string challenge) + { + if (!TryGetSupportedAuthType (challenge, out var authType)) { + throw new InvalidOperationException ($"Authenticaton scheme {authType} is not supported by {nameof (NTAuthenticationHelper)}."); + } + + return authType; + } + + static bool TryGetSupportedAuthType (string challenge, out string authType) + { + var spaceIndex = challenge.IndexOf (' '); + authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); + + return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || + authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); + } + + static async Task GetSpn ( + AndroidMessageHandler handler, + HttpRequestMessage request, + bool isProxyAuth, + CancellationToken cancellationToken) + { + // Calculate SPN (Service Principal Name) using the host name of the request. + // Use the request's 'Host' header if available. Otherwise, use the request uri. + // Ignore the 'Host' header if this is proxy authentication since we need to use + // the host name of the proxy itself for SPN calculation. + string hostName; + if (!isProxyAuth && request.Headers.Host != null) { + // Use the host name without any normalization. + hostName = request.Headers.Host; + } else { + var requestUri = request.RequestUri!; + var authUri = isProxyAuth ? handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; + + // Need to use FQDN normalized host so that CNAME's are traversed. + // Use DNS to do the forward lookup to an A (host) record. + // But skip DNS lookup on IP literals. Otherwise, we would end up + // doing an unintended reverse DNS lookup. + if (authUri.HostNameType == UriHostNameType.IPv6 || authUri.HostNameType == UriHostNameType.IPv4) { + hostName = authUri.IdnHost; + } else { + IPHostEntry result = await Dns.GetHostEntryAsync (authUri.IdnHost, cancellationToken).ConfigureAwait (false); + hostName = result.HostName; + } + } + + return $"HTTP/{hostName}"; + } + + static int GetRequestedContextFlags (bool isProxyAuth) + { + // the ContextFlagsPal is internal type in dotnet/runtime and we can't + // use it directly here so we have to use ints directly + int contextFlags = 0x00000800; // ContextFlagsPal.Connection + + // When connecting to proxy server don't enforce the integrity to avoid + // compatibility issues. The assumption is that the proxy server comes + // from a trusted source. + if (!isProxyAuth) { + contextFlags |= 0x00010000; // ContextFlagsPal.InitIntegrity + } + + return contextFlags; + } + + static bool TryGetChallenge ( + HttpResponseMessage response, + string authType, + bool isProxyAuth, + [NotNullWhen (true)] out string? challenge) + { + var responseHeaderValues = isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; + challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == authType)?.Parameter; + return !string.IsNullOrEmpty (challenge); + } + + static bool IsAuthenticationChallenge (HttpResponseMessage response, bool isProxyAuth) + => isProxyAuth + ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired + : response.StatusCode == HttpStatusCode.Unauthorized; + } +} diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs new file mode 100644 index 00000000000..8e4c4514583 --- /dev/null +++ b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs @@ -0,0 +1,86 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Reflection; +using System.Runtime; +using System.Security.Authentication.ExtendedProtection; + +namespace Xamarin.Android.Net +{ + internal sealed class NTAuthenticationProxy + { + const string AssemblyName = "System.Net.Http"; + const string TypeName = "System.Net.NTAuthentication"; + const string ContextFlagsPalTypeName = "System.Net.ContextFlagsPal"; + + const string ConstructorDescription = "#ctor(System.Boolean,System.String,System.Net.NetworkCredential,System.String,System.Net.ContextFlagsPal,System.Security.Authentication.ExtendedProtection.ChannelBinding)"; + const string IsCompletedPropertyName = "IsCompleted"; + const string GetOutgoingBlobMethodName = "GetOutgoingBlob"; + const string CloseContextMethodName = "CloseContext"; + + const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + + static Lazy s_NTAuthenticationType = new (() => FindType (TypeName, AssemblyName)); + static Lazy s_NTAuthenticationConstructorInfo = new (() => GetNTAuthenticationConstructor ()); + static Lazy s_IsCompletedPropertyInfo = new (() => GetProperty (IsCompletedPropertyName)); + static Lazy s_GetOutgoingBlobMethodInfo = new (() => GetMethod (GetOutgoingBlobMethodName)); + static Lazy s_CloseContextMethodInfo = new (() => GetMethod (CloseContextMethodName)); + + static Type FindType (string typeName, string assemblyName) + => Type.GetType ($"{typeName}, {assemblyName}", throwOnError: true)!; + + static ConstructorInfo GetNTAuthenticationConstructor () + { + var contextFlagsPalType = FindType (ContextFlagsPalTypeName, AssemblyName); + var paramTypes = new[] { + typeof (bool), + typeof (string), + typeof (NetworkCredential), + typeof (string), + contextFlagsPalType, + typeof (ChannelBinding) + }; + + return s_NTAuthenticationType.Value.GetConstructor (InstanceBindingFlags, paramTypes) + ?? throw new MissingMemberException (TypeName, ConstructorInfo.ConstructorName); + } + + static PropertyInfo GetProperty (string name) + => s_NTAuthenticationType.Value.GetProperty (name, InstanceBindingFlags) + ?? throw new MissingMemberException (TypeName, name); + + static MethodInfo GetMethod (string name) + => s_NTAuthenticationType.Value.GetMethod (name, InstanceBindingFlags) + ?? throw new MissingMemberException (TypeName, name); + + object _instance; + + [DynamicDependency (ConstructorDescription, TypeName, AssemblyName)] + internal NTAuthenticationProxy ( + bool isServer, + string package, + NetworkCredential credential, + string? spn, + int requestedContextFlags, + ChannelBinding? channelBinding) + { + var constructorParams = new object?[] { isServer, package, credential, spn, requestedContextFlags, channelBinding }; + _instance = s_NTAuthenticationConstructorInfo.Value.Invoke (constructorParams); + } + + public bool IsCompleted + => GetIsCompleted (); + + [DynamicDependency ($"get_{IsCompletedPropertyName}", TypeName, AssemblyName)] + bool GetIsCompleted () + => (bool)s_IsCompletedPropertyInfo.Value.GetValue (_instance); + + [DynamicDependency (GetOutgoingBlobMethodName, TypeName, AssemblyName)] + public string? GetOutgoingBlob (string? incomingBlob) + => (string?)s_GetOutgoingBlobMethodInfo.Value.Invoke (_instance, new object?[] { incomingBlob }); + + [DynamicDependency (CloseContextMethodName, TypeName, AssemblyName)] + public void CloseContext () + => s_CloseContextMethodInfo.Value.Invoke (_instance, null); + } +} From 532b35b5f9b605c430cc87255640d566ff05241b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 31 May 2022 11:48:51 +0200 Subject: [PATCH 02/33] Refactor NTAuthenticationHelper into NTAuthenticationHandler --- src/Mono.Android/Mono.Android.csproj | 3 +- .../AndroidMessageHandler.cs | 30 +- .../NTAuthenticationHandler.cs | 281 ++++++++++++++++++ .../NTAuthenticationHelper.cs | 193 ------------ .../NTAuthenticationProxy.cs | 86 ------ .../Mono.Android.NET-Tests.csproj | 2 + ...roidMessageHandlerNTAuthenticationTests.cs | 115 +++++++ 7 files changed, 426 insertions(+), 284 deletions(-) create mode 100644 src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs delete mode 100644 src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs delete mode 100644 src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs create mode 100644 tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 90608d45410..00763fdb500 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -367,9 +367,8 @@ - - + diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index 57f417185ae..f917db2bb16 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -139,11 +139,33 @@ public CookieContainer CookieContainer public bool UseProxy { get; set; } = true; - private bool CouldHaveNTCredentials => Proxy != null || Credentials != null; +#if !MONOANDROID1_0 + IWebProxy? _proxy; + ICredentials? _credentials; + NTAuthenticationHandler.Helper? _ntAuthHelper; + + public IWebProxy? Proxy + { + get => _proxy; + set { + _proxy = value; + _ntAuthHelper ??= new NTAuthenticationHandler.Helper (this); + } + } + public ICredentials? Credentials + { + get => _credentials; + set { + _credentials = value; + _ntAuthHelper ??= new NTAuthenticationHandler.Helper (this); + } + } +#else public IWebProxy? Proxy { get; set; } public ICredentials? Credentials { get; set; } +#endif public bool AllowAutoRedirect { get; set; } = true; @@ -338,8 +360,10 @@ string EncodeUrl (Uri url) var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); #if !MONOANDROID1_0 - if (CouldHaveNTCredentials && RequestNeedsAuthorization && NTAuthenticationHelper.TryGetSupportedAuthMethod (this, request, out var auth, out var credentials)) { - response = await NTAuthenticationHelper.SendAsync (this, request, response, auth, credentials, cancellationToken).ConfigureAwait (false); + if (RequestNeedsAuthorization && _ntAuthHelper != null && _ntAuthHelper.RequestNeedsNTAuthentication (request, out var ntAuth)) { + var authenticatedResponse = await ntAuth.ResendRequestWithAuthAsync (cancellationToken).ConfigureAwait (false); + if (authenticatedResponse != null) + return authenticatedResponse; } #endif diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs new file mode 100644 index 00000000000..ec9c6de9b44 --- /dev/null +++ b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Security.Authentication.ExtendedProtection; +using System.Threading; +using System.Threading.Tasks; + +namespace Xamarin.Android.Net +{ + // This code is heavily inspired by System.Net.Http.AuthenticationHelper + internal sealed class NTAuthenticationHandler + { + const int MaxRequests = 10; + + internal sealed class Helper + { + readonly AndroidMessageHandler _handler; + + internal Helper (AndroidMessageHandler handler) + { + _handler = handler; + } + + internal bool RequestNeedsNTAuthentication (HttpRequestMessage request, [NotNullWhen (true)] out NTAuthenticationHandler? ntAuthHandler) + { + IEnumerable requestedAuthentication = _handler.RequestedAuthentication ?? Enumerable.Empty (); + foreach (var auth in requestedAuthentication) { + if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { + var credentials = auth.UseProxyAuthentication ? _handler.Proxy?.Credentials : _handler.Credentials; + var correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); + + if (correspondingCredentials != null) { + ntAuthHandler = new NTAuthenticationHandler (_handler, request, authType, auth.UseProxyAuthentication, correspondingCredentials); + return true; + } + } + } + + ntAuthHandler = null; + return false; + } + + static bool TryGetSupportedAuthType (string challenge, out string authType) + { + var spaceIndex = challenge.IndexOf (' '); + authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); + + return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || + authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); + } + } + + readonly AndroidMessageHandler _handler; + readonly HttpRequestMessage _request; + readonly string _authType; + readonly bool _isProxyAuth; + readonly NetworkCredential _credentials; + + private NTAuthenticationHandler ( + AndroidMessageHandler handler, + HttpRequestMessage request, + string authType, + bool isProxyAuth, + NetworkCredential credentials) + { + _handler = handler; + _request = request; + _authType = authType; + _isProxyAuth = isProxyAuth; + _credentials = credentials; + } + + internal async Task ResendRequestWithAuthAsync (CancellationToken cancellationToken) + { + var authContext = new NTAuthentication ( + isServer: false, + _authType, + _credentials, + spn: await GetSpn (cancellationToken).ConfigureAwait (false), + requestedContextFlags: GetRequestedContextFlags (), + channelBinding: null); + + // we need to make sure that the handler doesn't override the authorization header + // with the user defined pre-authentication data + var originalPreAuthenticate = _handler.PreAuthenticate; + _handler.PreAuthenticate = false; + + try { + return await DoResendRequestWithAuthAsync (authContext, cancellationToken); + } finally { + _handler.PreAuthenticate = originalPreAuthenticate; + authContext.CloseContext (); + } + } + + async Task DoResendRequestWithAuthAsync (NTAuthentication authContext, CancellationToken cancellationToken) + { + HttpResponseMessage? response = null; + + string? challenge = null; + int requestCounter = 0; + + while (requestCounter++ < MaxRequests) { + var challengeResponse = authContext.GetOutgoingBlob (challenge); + if (challengeResponse == null) { + // response indicated denial even after login, so stop processing + // and return current response + break; + } + + var headerValue = new AuthenticationHeaderValue (_authType, challengeResponse); + if (_isProxyAuth) { + _request.Headers.ProxyAuthorization = headerValue; + } else { + _request.Headers.Authorization = headerValue; + } + + response = await _handler.DoSendAsync (_request, cancellationToken).ConfigureAwait (false); + + // we need to drain the content otherwise the next request + // won't reuse the same TCP socket and persistent auth won't work + await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); + + if (authContext.IsCompleted || !TryGetChallenge (response, out challenge)) { + break; + } + + if (!IsAuthenticationChallenge (response)) { + // Tail response for Negotiate on successful authentication. + // Validate it before we proceed. + authContext.GetOutgoingBlob (challenge); + break; + } + } + + return response; + } + + async Task GetSpn (CancellationToken cancellationToken) + { + var hostName = await GetHostName (cancellationToken); + return $"HTTP/{hostName}"; + } + + async Task GetHostName (CancellationToken cancellationToken) + { + // Calculate SPN (Service Principal Name) using the host name of the request. + // Use the request's 'Host' header if available. Otherwise, use the request uri. + // Ignore the 'Host' header if this is proxy authentication since we need to use + // the host name of the proxy itself for SPN calculation. + if (!_isProxyAuth && _request.Headers.Host != null) { + // Use the host name without any normalization. + return _request.Headers.Host; + } + + var requestUri = _request.RequestUri!; + var authUri = _isProxyAuth ? _handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; + + // Need to use FQDN normalized host so that CNAME's are traversed. + // Use DNS to do the forward lookup to an A (host) record. + // But skip DNS lookup on IP literals. Otherwise, we would end up + // doing an unintended reverse DNS lookup. + if (authUri.HostNameType == UriHostNameType.IPv6 || authUri.HostNameType == UriHostNameType.IPv4) { + return authUri.IdnHost; + } else { + IPHostEntry result = await Dns.GetHostEntryAsync (authUri.IdnHost, cancellationToken).ConfigureAwait (false); + return result.HostName; + } + } + + int GetRequestedContextFlags () + { + // the ContextFlagsPal is internal type in dotnet/runtime and we can't + // use it directly here so we have to use ints directly + int contextFlags = 0x00000800; // ContextFlagsPal.Connection + + // When connecting to proxy server don't enforce the integrity to avoid + // compatibility issues. The assumption is that the proxy server comes + // from a trusted source. + if (!_isProxyAuth) { + contextFlags |= 0x00010000; // ContextFlagsPal.InitIntegrity + } + + return contextFlags; + } + + bool TryGetChallenge (HttpResponseMessage response, [NotNullWhen (true)] out string? challenge) + { + var responseHeaderValues = _isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; + challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == _authType)?.Parameter; + return !string.IsNullOrEmpty (challenge); + } + + bool IsAuthenticationChallenge (HttpResponseMessage response) + => _isProxyAuth + ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired + : response.StatusCode == HttpStatusCode.Unauthorized; + + private sealed class NTAuthentication + { + const string AssemblyName = "System.Net.Http"; + const string TypeName = "System.Net.NTAuthentication"; + const string ContextFlagsPalTypeName = "System.Net.ContextFlagsPal"; + + const string ConstructorDescription = "#ctor(System.Boolean,System.String,System.Net.NetworkCredential,System.String,System.Net.ContextFlagsPal,System.Security.Authentication.ExtendedProtection.ChannelBinding)"; + const string IsCompletedPropertyName = "IsCompleted"; + const string GetOutgoingBlobMethodName = "GetOutgoingBlob"; + const string CloseContextMethodName = "CloseContext"; + + const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + + static Lazy s_NTAuthenticationType = new (() => FindType (TypeName, AssemblyName)); + static Lazy s_NTAuthenticationConstructorInfo = new (() => GetNTAuthenticationConstructor ()); + static Lazy s_IsCompletedPropertyInfo = new (() => GetProperty (IsCompletedPropertyName)); + static Lazy s_GetOutgoingBlobMethodInfo = new (() => GetMethod (GetOutgoingBlobMethodName)); + static Lazy s_CloseContextMethodInfo = new (() => GetMethod (CloseContextMethodName)); + + static Type FindType (string typeName, string assemblyName) + => Type.GetType ($"{typeName}, {assemblyName}", throwOnError: true)!; + + static ConstructorInfo GetNTAuthenticationConstructor () + { + var contextFlagsPalType = FindType (ContextFlagsPalTypeName, AssemblyName); + var paramTypes = new[] { + typeof (bool), + typeof (string), + typeof (NetworkCredential), + typeof (string), + contextFlagsPalType, + typeof (ChannelBinding) + }; + + return s_NTAuthenticationType.Value.GetConstructor (InstanceBindingFlags, paramTypes) + ?? throw new MissingMemberException (TypeName, ConstructorInfo.ConstructorName); + } + + static PropertyInfo GetProperty (string name) + => s_NTAuthenticationType.Value.GetProperty (name, InstanceBindingFlags) + ?? throw new MissingMemberException (TypeName, name); + + static MethodInfo GetMethod (string name) + => s_NTAuthenticationType.Value.GetMethod (name, InstanceBindingFlags) + ?? throw new MissingMemberException (TypeName, name); + + object _instance; + + [DynamicDependency (ConstructorDescription, TypeName, AssemblyName)] + internal NTAuthentication ( + bool isServer, + string package, + NetworkCredential credential, + string? spn, + int requestedContextFlags, + ChannelBinding? channelBinding) + { + var constructorParams = new object?[] { isServer, package, credential, spn, requestedContextFlags, channelBinding }; + _instance = s_NTAuthenticationConstructorInfo.Value.Invoke (constructorParams); + } + + public bool IsCompleted + => GetIsCompleted (); + + [DynamicDependency ($"get_{IsCompletedPropertyName}", TypeName, AssemblyName)] + bool GetIsCompleted () + => (bool)(s_IsCompletedPropertyInfo.Value.GetValue (_instance) ?? false); + + [DynamicDependency (GetOutgoingBlobMethodName, TypeName, AssemblyName)] + public string? GetOutgoingBlob (string? incomingBlob) + => (string?)s_GetOutgoingBlobMethodInfo.Value.Invoke (_instance, new object?[] { incomingBlob }); + + [DynamicDependency (CloseContextMethodName, TypeName, AssemblyName)] + public void CloseContext () + => s_CloseContextMethodInfo.Value.Invoke (_instance, null); + } + } +} diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs deleted file mode 100644 index 4a1ed91bae0..00000000000 --- a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHelper.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Security.Authentication.ExtendedProtection; -using System.Threading; -using System.Threading.Tasks; - -namespace Xamarin.Android.Net -{ - // This code is heavily inspired by System.Net.Http.AuthenticationHelper - internal static class NTAuthenticationHelper - { - const int MaxRequests = 10; - - internal static bool TryGetSupportedAuthMethod ( - AndroidMessageHandler handler, - HttpRequestMessage request, - [NotNullWhen (true)] out AuthenticationData? supportedAuth, - [NotNullWhen (true)] out NetworkCredential? suitableCredentials) - { - IEnumerable requestedAuthentication = handler.RequestedAuthentication ?? Enumerable.Empty (); - foreach (var auth in requestedAuthentication) { - if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { - var credentials = auth.UseProxyAuthentication ? handler.Proxy?.Credentials : handler.Credentials; - suitableCredentials = credentials?.GetCredential (request.RequestUri, authType); - - if (suitableCredentials != null) { - supportedAuth = auth; - return true; - } - } - } - - supportedAuth = null; - suitableCredentials = null; - return false; - } - - internal static async Task SendAsync ( - AndroidMessageHandler handler, - HttpRequestMessage request, - HttpResponseMessage response, - AuthenticationData auth, - NetworkCredential credentials, - CancellationToken cancellationToken) - { - var authType = GetSupportedAuthType (auth.Challenge); - var isProxyAuth = auth.UseProxyAuthentication; - var authContext = new NTAuthenticationProxy ( - isServer: false, - authType, - credentials, - spn: await GetSpn (handler, request, isProxyAuth, cancellationToken).ConfigureAwait (false), - requestedContextFlags: GetRequestedContextFlags (isProxyAuth), - channelBinding: null); - - // we need to make sure that the handler doesn't override the authorization header - // with the user defined pre-authentication data - var originalPreAuthenticate = handler.PreAuthenticate; - handler.PreAuthenticate = false; - - try { - string? challenge = null; - int requestCounter = 0; - - while (requestCounter++ < MaxRequests) { - var challengeResponse = authContext.GetOutgoingBlob (challenge); - if (challengeResponse == null) { - // response indicated denial even after login, so stop processing - // and return current response - break; - } - - var headerValue = new AuthenticationHeaderValue (authType, challengeResponse); - if (auth.UseProxyAuthentication) { - request.Headers.ProxyAuthorization = headerValue; - } else { - request.Headers.Authorization = headerValue; - } - - response = await handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); - - // we need to drain the content otherwise the next request - // won't reuse the same TCP socket and persistent auth won't work - await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); - - if (authContext.IsCompleted || !TryGetChallenge (response, authType, isProxyAuth, out challenge)) { - break; - } - - if (!IsAuthenticationChallenge (response, isProxyAuth)) { - // Tail response for Negotiate on successful authentication. - // Validate it before we proceed. - authContext.GetOutgoingBlob (challenge); - break; - } - } - - return response; - } finally { - handler.PreAuthenticate = originalPreAuthenticate; - authContext.CloseContext (); - } - } - - static string GetSupportedAuthType (string challenge) - { - if (!TryGetSupportedAuthType (challenge, out var authType)) { - throw new InvalidOperationException ($"Authenticaton scheme {authType} is not supported by {nameof (NTAuthenticationHelper)}."); - } - - return authType; - } - - static bool TryGetSupportedAuthType (string challenge, out string authType) - { - var spaceIndex = challenge.IndexOf (' '); - authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); - - return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || - authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); - } - - static async Task GetSpn ( - AndroidMessageHandler handler, - HttpRequestMessage request, - bool isProxyAuth, - CancellationToken cancellationToken) - { - // Calculate SPN (Service Principal Name) using the host name of the request. - // Use the request's 'Host' header if available. Otherwise, use the request uri. - // Ignore the 'Host' header if this is proxy authentication since we need to use - // the host name of the proxy itself for SPN calculation. - string hostName; - if (!isProxyAuth && request.Headers.Host != null) { - // Use the host name without any normalization. - hostName = request.Headers.Host; - } else { - var requestUri = request.RequestUri!; - var authUri = isProxyAuth ? handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; - - // Need to use FQDN normalized host so that CNAME's are traversed. - // Use DNS to do the forward lookup to an A (host) record. - // But skip DNS lookup on IP literals. Otherwise, we would end up - // doing an unintended reverse DNS lookup. - if (authUri.HostNameType == UriHostNameType.IPv6 || authUri.HostNameType == UriHostNameType.IPv4) { - hostName = authUri.IdnHost; - } else { - IPHostEntry result = await Dns.GetHostEntryAsync (authUri.IdnHost, cancellationToken).ConfigureAwait (false); - hostName = result.HostName; - } - } - - return $"HTTP/{hostName}"; - } - - static int GetRequestedContextFlags (bool isProxyAuth) - { - // the ContextFlagsPal is internal type in dotnet/runtime and we can't - // use it directly here so we have to use ints directly - int contextFlags = 0x00000800; // ContextFlagsPal.Connection - - // When connecting to proxy server don't enforce the integrity to avoid - // compatibility issues. The assumption is that the proxy server comes - // from a trusted source. - if (!isProxyAuth) { - contextFlags |= 0x00010000; // ContextFlagsPal.InitIntegrity - } - - return contextFlags; - } - - static bool TryGetChallenge ( - HttpResponseMessage response, - string authType, - bool isProxyAuth, - [NotNullWhen (true)] out string? challenge) - { - var responseHeaderValues = isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; - challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == authType)?.Parameter; - return !string.IsNullOrEmpty (challenge); - } - - static bool IsAuthenticationChallenge (HttpResponseMessage response, bool isProxyAuth) - => isProxyAuth - ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired - : response.StatusCode == HttpStatusCode.Unauthorized; - } -} diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs deleted file mode 100644 index 8e4c4514583..00000000000 --- a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationProxy.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Net; -using System.Reflection; -using System.Runtime; -using System.Security.Authentication.ExtendedProtection; - -namespace Xamarin.Android.Net -{ - internal sealed class NTAuthenticationProxy - { - const string AssemblyName = "System.Net.Http"; - const string TypeName = "System.Net.NTAuthentication"; - const string ContextFlagsPalTypeName = "System.Net.ContextFlagsPal"; - - const string ConstructorDescription = "#ctor(System.Boolean,System.String,System.Net.NetworkCredential,System.String,System.Net.ContextFlagsPal,System.Security.Authentication.ExtendedProtection.ChannelBinding)"; - const string IsCompletedPropertyName = "IsCompleted"; - const string GetOutgoingBlobMethodName = "GetOutgoingBlob"; - const string CloseContextMethodName = "CloseContext"; - - const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - - static Lazy s_NTAuthenticationType = new (() => FindType (TypeName, AssemblyName)); - static Lazy s_NTAuthenticationConstructorInfo = new (() => GetNTAuthenticationConstructor ()); - static Lazy s_IsCompletedPropertyInfo = new (() => GetProperty (IsCompletedPropertyName)); - static Lazy s_GetOutgoingBlobMethodInfo = new (() => GetMethod (GetOutgoingBlobMethodName)); - static Lazy s_CloseContextMethodInfo = new (() => GetMethod (CloseContextMethodName)); - - static Type FindType (string typeName, string assemblyName) - => Type.GetType ($"{typeName}, {assemblyName}", throwOnError: true)!; - - static ConstructorInfo GetNTAuthenticationConstructor () - { - var contextFlagsPalType = FindType (ContextFlagsPalTypeName, AssemblyName); - var paramTypes = new[] { - typeof (bool), - typeof (string), - typeof (NetworkCredential), - typeof (string), - contextFlagsPalType, - typeof (ChannelBinding) - }; - - return s_NTAuthenticationType.Value.GetConstructor (InstanceBindingFlags, paramTypes) - ?? throw new MissingMemberException (TypeName, ConstructorInfo.ConstructorName); - } - - static PropertyInfo GetProperty (string name) - => s_NTAuthenticationType.Value.GetProperty (name, InstanceBindingFlags) - ?? throw new MissingMemberException (TypeName, name); - - static MethodInfo GetMethod (string name) - => s_NTAuthenticationType.Value.GetMethod (name, InstanceBindingFlags) - ?? throw new MissingMemberException (TypeName, name); - - object _instance; - - [DynamicDependency (ConstructorDescription, TypeName, AssemblyName)] - internal NTAuthenticationProxy ( - bool isServer, - string package, - NetworkCredential credential, - string? spn, - int requestedContextFlags, - ChannelBinding? channelBinding) - { - var constructorParams = new object?[] { isServer, package, credential, spn, requestedContextFlags, channelBinding }; - _instance = s_NTAuthenticationConstructorInfo.Value.Invoke (constructorParams); - } - - public bool IsCompleted - => GetIsCompleted (); - - [DynamicDependency ($"get_{IsCompletedPropertyName}", TypeName, AssemblyName)] - bool GetIsCompleted () - => (bool)s_IsCompletedPropertyInfo.Value.GetValue (_instance); - - [DynamicDependency (GetOutgoingBlobMethodName, TypeName, AssemblyName)] - public string? GetOutgoingBlob (string? incomingBlob) - => (string?)s_GetOutgoingBlobMethodInfo.Value.Invoke (_instance, new object?[] { incomingBlob }); - - [DynamicDependency (CloseContextMethodName, TypeName, AssemblyName)] - public void CloseContext () - => s_CloseContextMethodInfo.Value.Invoke (_instance, null); - } -} diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index 4bf8b2ef988..9aafe7ec29c 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -40,6 +40,8 @@ + + diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs new file mode 100644 index 00000000000..d68ff12c0a7 --- /dev/null +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs @@ -0,0 +1,115 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Xamarin.Android.Net; +using NUnit.Framework; + +namespace Xamarin.Android.NetTests { + [TestFixture] + public sealed class AndroidMessageHandlerNTAuthenticationTests + { + [Test] + public async Task RequestWithoutCredentialsFails () + { + using var server = new FakeNtlmServer (port: 47662); + var handler = new AndroidMessageHandler (); + var client = new HttpClient (handler); + + var response = await client.GetAsync (server.Uri); + + Assert.IsFalse (response.IsSuccessStatusCode); + Assert.AreEqual (HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Test] + public async Task RequestWithCredentialsSucceeds () + { + using var server = new FakeNtlmServer (port: 47663); + var cache = new CredentialCache (); + cache.Add (server.Uri, "NTLM", FakeNtlmServer.Credentials); + var handler = new AndroidMessageHandler { Credentials = cache }; + var client = new HttpClient (handler); + + var response = await client.GetAsync (server.Uri); + var content = await response.Content.ReadAsStringAsync (); + + Assert.IsTrue (response.IsSuccessStatusCode); + Assert.AreEqual (FakeNtlmServer.SecretContent, content); + } + + sealed class FakeNtlmServer : IDisposable + { + public static readonly NetworkCredential Credentials = new NetworkCredential ("User", "Password", "Domain"); + public static readonly string SecretContent = "SECRET"; + + HttpListener? _listener = new HttpListener (); + Task? _loop; + + public FakeNtlmServer (int port) + { + Uri = new Uri ($"http://localhost:{port}/"); + + _listener.Prefixes.Add ($"http://+:{port}/"); + _listener.Start (); + _loop = Task.Run (Loop); + } + + public Uri Uri { get; } + + public void Dispose () + { + _listener?.Close (); + _listener = null; + + _loop?.GetAwaiter ().GetResult (); + _loop = null; + } + + async Task Loop () + { + try { + while (true) { + var ctx = await _listener!.GetContextAsync (); + var authorization = ctx.Request.Headers.Get ("Authorization"); + var fakeResponse = Handle (authorization); + fakeResponse.ConfigureAndClose (ctx.Response); + } + } catch (ObjectDisposedException) { + // this exception is expected when the listener is closed + } catch (HttpListenerException) { + // shut down the listener + } + } + + const string ntlm = "NTLM"; + const string initiation = "NTLM TlRMTVNTUAABAAAAFYKIYgAAAAAAAAAAAAAAAAAAAAAGAbAdAAAADw=="; + const string challenge = "NTLM TlRMTVNTUAACAAAADAAMADgAAAAVgoliASNFZ4mrze8AAAAAAAAAADAAMABEAAAABgBwFwAAAA9EAG8AbQBhAGkAbgACAAwARABvAG0AYQBpAG4AAQAMAFMAZQByAHYAZQByAAcACADffWrlcGTYAQAAAAA="; + const string challengeResponsePrefix = "NTLM TlRMTVNTUAADAAAAGAAYAFgAAACcAJwAcAAAAAwADAAUAQAACAAIAAwBAAASABIAIAEAABAAEAAyAQAAFYKIYgYBsB0AAAAP"; + + // 1. the client makes an unauthenticated request + // -> the server responds to with the "WWW-Authenticate: NTLM" header + // 2. the client sends a request with the "Authorization: NTLM " header + // -> the server responds with the "WWW-Authenticate: NTLM " header + // 3. the client responds with the "Authorization: NTLM " header + // -> the server returns 200 + static FakeResponse Handle (string? authorization) + => authorization switch { + initiation => new (HttpStatusCode.Unauthorized, challenge, string.Empty), + string challengeResponse when challengeResponse.StartsWith (challengeResponsePrefix) => new (HttpStatusCode.OK, null, SecretContent), + _ => new (HttpStatusCode.Unauthorized, ntlm, string.Empty) + }; + + record FakeResponse (HttpStatusCode statusCode, string? header, string body) + { + public void ConfigureAndClose (HttpListenerResponse res) + { + res.StatusCode = (int)statusCode; + if (header != null) res.AddHeader ("WWW-Authenticate", header); + res.Close (Encoding.UTF8.GetBytes (body), false); + } + } + } + } +} From 449151eaf74a885127f9088df1f86bc9aa9161e7 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 2 Jun 2022 10:25:23 +0200 Subject: [PATCH 03/33] Update apkdesc files --- .../BuildReleaseArm64SimpleDotNet.apkdesc | 64 +- .../BuildReleaseArm64SimpleLegacy.apkdesc | 26 +- .../BuildReleaseArm64XFormsDotNet.apkdesc | 37 +- .../BuildReleaseArm64XFormsLegacy.apkdesc | 940 +++++++++--------- 4 files changed, 559 insertions(+), 508 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc index 0ead66e3aa6..f65d32556e0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc @@ -5,34 +5,76 @@ "Size": 3032 }, "assemblies/Java.Interop.dll": { - "Size": 59377 + "Size": 60761 }, "assemblies/Mono.Android.dll": { - "Size": 89098 + "Size": 156719 }, "assemblies/rc.bin": { "Size": 1083 }, + "assemblies/System.Collections.Concurrent.dll": { + "Size": 9171 + }, + "assemblies/System.Collections.dll": { + "Size": 4282 + }, + "assemblies/System.Collections.NonGeneric.dll": { + "Size": 6236 + }, "assemblies/System.Console.dll": { - "Size": 6475 + "Size": 6506 + }, + "assemblies/System.Formats.Asn1.dll": { + "Size": 17426 + }, + "assemblies/System.IO.Compression.dll": { + "Size": 16266 }, "assemblies/System.Linq.dll": { - "Size": 9971 + "Size": 10316 + }, + "assemblies/System.Net.Http.dll": { + "Size": 75341 + }, + "assemblies/System.Net.NameResolution.dll": { + "Size": 12592 + }, + "assemblies/System.Net.Primitives.dll": { + "Size": 32401 + }, + "assemblies/System.Net.Requests.dll": { + "Size": 3519 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 524631 + "Size": 621008 + }, + "assemblies/System.Private.Uri.dll": { + "Size": 37777 + }, + "assemblies/System.Private.Xml.dll": { + "Size": 140803 }, "assemblies/System.Runtime.dll": { "Size": 2410 }, + "assemblies/System.Runtime.Numerics.dll": { + "Size": 23826 + }, + "assemblies/System.Security.Cryptography.dll": { + "Size": 9671 + }, + "assemblies/System.Text.RegularExpressions.dll": { + "Size": 9593 + }, "assemblies/UnnamedProject.dll": { - "Size": 3553 + "Size": 3550 }, "classes.dex": { "Size": 347544 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 512552 + "Size": 484128 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667768 @@ -47,16 +89,16 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 9488 + "Size": 16208 }, "META-INF/BNDLTOOL.RSA": { "Size": 1213 }, "META-INF/BNDLTOOL.SF": { - "Size": 2570 + "Size": 4101 }, "META-INF/MANIFEST.MF": { - "Size": 2443 + "Size": 3974 }, "res/drawable-hdpi-v4/icon.png": { "Size": 4791 @@ -83,5 +125,5 @@ "Size": 1904 } }, - "PackageSize": 3020767 + "PackageSize": 3583086 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc index a0a7e65caa1..8830d536242 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc @@ -5,43 +5,43 @@ "Size": 2604 }, "assemblies/Java.Interop.dll": { - "Size": 67950 + "Size": 68893 }, "assemblies/Mono.Android.dll": { - "Size": 257187 + "Size": 256736 }, "assemblies/mscorlib.dll": { - "Size": 769016 + "Size": 769010 }, "assemblies/System.Core.dll": { - "Size": 28198 + "Size": 28189 }, "assemblies/System.dll": { - "Size": 9180 + "Size": 9178 }, "assemblies/UnnamedProject.dll": { - "Size": 2879 + "Size": 2870 }, "classes.dex": { - "Size": 349736 + "Size": 350636 }, "lib/arm64-v8a/libmono-btls-shared.so": { "Size": 1613872 }, + "lib/arm64-v8a/libmonodroid.so": { + "Size": 394504 + }, "lib/arm64-v8a/libmono-native.so": { "Size": 750976 }, - "lib/arm64-v8a/libmonodroid.so": { - "Size": 421872 - }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 4030448 + "Size": 4051864 }, "lib/arm64-v8a/libxa-internal-api.so": { "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 19872 + "Size": 19864 }, "META-INF/ANDROIDD.RSA": { "Size": 1213 @@ -74,5 +74,5 @@ "Size": 1724 } }, - "PackageSize": 4052692 + "PackageSize": 4048596 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc index 96a6684a9b7..32b8ec89b7f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc @@ -8,10 +8,10 @@ "Size": 7114 }, "assemblies/Java.Interop.dll": { - "Size": 66738 + "Size": 66817 }, "assemblies/Mono.Android.dll": { - "Size": 444811 + "Size": 451768 }, "assemblies/mscorlib.dll": { "Size": 3891 @@ -26,7 +26,7 @@ "Size": 10777 }, "assemblies/System.Collections.dll": { - "Size": 16153 + "Size": 16213 }, "assemblies/System.Collections.NonGeneric.dll": { "Size": 7932 @@ -58,6 +58,9 @@ "assemblies/System.Drawing.Primitives.dll": { "Size": 12123 }, + "assemblies/System.Formats.Asn1.dll": { + "Size": 17426 + }, "assemblies/System.IO.Compression.dll": { "Size": 16741 }, @@ -71,10 +74,13 @@ "Size": 177473 }, "assemblies/System.Net.Http.dll": { - "Size": 65645 + "Size": 75831 + }, + "assemblies/System.Net.NameResolution.dll": { + "Size": 12592 }, "assemblies/System.Net.Primitives.dll": { - "Size": 22070 + "Size": 33309 }, "assemblies/System.Net.Requests.dll": { "Size": 3660 @@ -83,10 +89,10 @@ "Size": 8540 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 800901 + "Size": 802808 }, "assemblies/System.Private.DataContractSerialization.dll": { - "Size": 185328 + "Size": 185395 }, "assemblies/System.Private.Uri.dll": { "Size": 42655 @@ -100,6 +106,9 @@ "assemblies/System.Runtime.dll": { "Size": 2603 }, + "assemblies/System.Runtime.Numerics.dll": { + "Size": 33362 + }, "assemblies/System.Runtime.Serialization.dll": { "Size": 2020 }, @@ -110,7 +119,7 @@ "Size": 3834 }, "assemblies/System.Security.Cryptography.dll": { - "Size": 7751 + "Size": 10020 }, "assemblies/System.Text.RegularExpressions.dll": { "Size": 159573 @@ -119,7 +128,7 @@ "Size": 1910 }, "assemblies/UnnamedProject.dll": { - "Size": 117243 + "Size": 117241 }, "assemblies/Xamarin.AndroidX.Activity.dll": { "Size": 5941 @@ -191,7 +200,7 @@ "Size": 3460156 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 512552 + "Size": 484128 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667768 @@ -206,7 +215,7 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 98592 + "Size": 98744 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -320,13 +329,13 @@ "Size": 1213 }, "META-INF/BNDLTOOL.SF": { - "Size": 78757 + "Size": 79085 }, "META-INF/com.google.android.material_material.version": { "Size": 10 }, "META-INF/MANIFEST.MF": { - "Size": 78630 + "Size": 78958 }, "META-INF/proguard/androidx-annotations.pro": { "Size": 339 @@ -1961,5 +1970,5 @@ "Size": 341228 } }, - "PackageSize": 8347981 + "PackageSize": 8442439 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc index 691f59a584e..8b15f83e368 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc @@ -5,133 +5,133 @@ "Size": 3140 }, "assemblies/FormsViewGroup.dll": { - "Size": 7215 + "Size": 7207 }, "assemblies/Java.Interop.dll": { - "Size": 68916 + "Size": 69916 }, "assemblies/Mono.Android.dll": { - "Size": 567711 + "Size": 567807 }, "assemblies/Mono.Security.dll": { - "Size": 68432 + "Size": 68429 }, "assemblies/mscorlib.dll": { - "Size": 915405 + "Size": 915392 }, "assemblies/System.Core.dll": { - "Size": 164046 + "Size": 164045 }, "assemblies/System.dll": { - "Size": 388864 + "Size": 388861 }, "assemblies/System.Drawing.Common.dll": { - "Size": 12365 + "Size": 12356 }, "assemblies/System.Net.Http.dll": { - "Size": 110642 + "Size": 110693 }, "assemblies/System.Numerics.dll": { - "Size": 15683 + "Size": 15681 }, "assemblies/System.Runtime.Serialization.dll": { - "Size": 186660 + "Size": 186653 }, "assemblies/System.ServiceModel.Internals.dll": { - "Size": 26593 + "Size": 26585 }, "assemblies/System.Xml.dll": { - "Size": 395657 + "Size": 395651 }, "assemblies/UnnamedProject.dll": { - "Size": 116898 + "Size": 116888 }, "assemblies/Xamarin.AndroidX.Activity.dll": { - "Size": 7697 + "Size": 7689 }, "assemblies/Xamarin.AndroidX.AppCompat.AppCompatResources.dll": { - "Size": 6648 + "Size": 6640 }, "assemblies/Xamarin.AndroidX.AppCompat.dll": { - "Size": 125328 + "Size": 125325 }, "assemblies/Xamarin.AndroidX.CardView.dll": { - "Size": 7367 + "Size": 7357 }, "assemblies/Xamarin.AndroidX.CoordinatorLayout.dll": { - "Size": 18272 + "Size": 18264 }, "assemblies/Xamarin.AndroidX.Core.dll": { - "Size": 131930 + "Size": 131924 }, "assemblies/Xamarin.AndroidX.DrawerLayout.dll": { - "Size": 15425 + "Size": 15422 }, "assemblies/Xamarin.AndroidX.Fragment.dll": { - "Size": 43134 + "Size": 43130 }, "assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": { - "Size": 6714 + "Size": 6708 }, "assemblies/Xamarin.AndroidX.Lifecycle.Common.dll": { - "Size": 7062 + "Size": 7055 }, "assemblies/Xamarin.AndroidX.Lifecycle.LiveData.Core.dll": { - "Size": 7194 + "Size": 7185 }, "assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": { - "Size": 4873 + "Size": 4862 }, "assemblies/Xamarin.AndroidX.Loader.dll": { - "Size": 13585 + "Size": 13578 }, "assemblies/Xamarin.AndroidX.RecyclerView.dll": { - "Size": 102327 + "Size": 102322 }, "assemblies/Xamarin.AndroidX.SavedState.dll": { - "Size": 6268 + "Size": 6265 }, "assemblies/Xamarin.AndroidX.SwipeRefreshLayout.dll": { - "Size": 11272 + "Size": 11261 }, "assemblies/Xamarin.AndroidX.ViewPager.dll": { - "Size": 19424 + "Size": 19416 }, "assemblies/Xamarin.Forms.Core.dll": { - "Size": 524736 + "Size": 524728 }, "assemblies/Xamarin.Forms.Platform.Android.dll": { - "Size": 384872 + "Size": 384861 }, "assemblies/Xamarin.Forms.Platform.dll": { "Size": 56878 }, "assemblies/Xamarin.Forms.Xaml.dll": { - "Size": 55801 + "Size": 55795 }, "assemblies/Xamarin.Google.Android.Material.dll": { - "Size": 43497 + "Size": 43489 }, "classes.dex": { - "Size": 3462080 + "Size": 3462744 }, "lib/arm64-v8a/libmono-btls-shared.so": { "Size": 1613872 }, + "lib/arm64-v8a/libmonodroid.so": { + "Size": 394504 + }, "lib/arm64-v8a/libmono-native.so": { "Size": 750976 }, - "lib/arm64-v8a/libmonodroid.so": { - "Size": 421872 - }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 4030448 + "Size": 4051864 }, "lib/arm64-v8a/libxa-internal-api.so": { "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 104928 + "Size": 104920 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -145,10 +145,10 @@ "META-INF/androidx.activity_activity.version": { "Size": 6 }, - "META-INF/androidx.appcompat_appcompat-resources.version": { + "META-INF/androidx.appcompat_appcompat.version": { "Size": 6 }, - "META-INF/androidx.appcompat_appcompat.version": { + "META-INF/androidx.appcompat_appcompat-resources.version": { "Size": 6 }, "META-INF/androidx.arch.core_core-runtime.version": { @@ -196,10 +196,10 @@ "META-INF/androidx.legacy_legacy-support-v4.version": { "Size": 6 }, - "META-INF/androidx.lifecycle_lifecycle-livedata-core.version": { + "META-INF/androidx.lifecycle_lifecycle-livedata.version": { "Size": 6 }, - "META-INF/androidx.lifecycle_lifecycle-livedata.version": { + "META-INF/androidx.lifecycle_lifecycle-livedata-core.version": { "Size": 6 }, "META-INF/androidx.lifecycle_lifecycle-runtime.version": { @@ -235,10 +235,10 @@ "META-INF/androidx.transition_transition.version": { "Size": 6 }, - "META-INF/androidx.vectordrawable_vectordrawable-animated.version": { + "META-INF/androidx.vectordrawable_vectordrawable.version": { "Size": 6 }, - "META-INF/androidx.vectordrawable_vectordrawable.version": { + "META-INF/androidx.vectordrawable_vectordrawable-animated.version": { "Size": 6 }, "META-INF/androidx.versionedparcelable_versionedparcelable.version": { @@ -256,12 +256,6 @@ "META-INF/proguard/androidx-annotations.pro": { "Size": 339 }, - "res/anim-v21/design_bottom_sheet_slide_in.xml": { - "Size": 616 - }, - "res/anim-v21/design_bottom_sheet_slide_out.xml": { - "Size": 616 - }, "res/anim/abc_fade_in.xml": { "Size": 388 }, @@ -358,9 +352,6 @@ "res/anim/exittoright.xml": { "Size": 468 }, - "res/animator-v21/design_appbar_state_list_animator.xml": { - "Size": 1216 - }, "res/animator/design_fab_hide_motion_spec.xml": { "Size": 796 }, @@ -388,38 +379,14 @@ "res/animator/mtrl_fab_transformation_sheet_expand_spec.xml": { "Size": 1888 }, - "res/color-v21/abc_btn_colored_borderless_text_material.xml": { - "Size": 464 - }, - "res/color-v23/abc_btn_colored_borderless_text_material.xml": { - "Size": 500 - }, - "res/color-v23/abc_btn_colored_text_material.xml": { - "Size": 500 - }, - "res/color-v23/abc_color_highlight_material.xml": { - "Size": 544 - }, - "res/color-v23/abc_tint_btn_checkable.xml": { - "Size": 624 - }, - "res/color-v23/abc_tint_default.xml": { - "Size": 1120 - }, - "res/color-v23/abc_tint_edittext.xml": { - "Size": 668 - }, - "res/color-v23/abc_tint_seek_thumb.xml": { - "Size": 500 - }, - "res/color-v23/abc_tint_spinner.xml": { - "Size": 668 + "res/animator-v21/design_appbar_state_list_animator.xml": { + "Size": 1216 }, - "res/color-v23/abc_tint_switch_track.xml": { - "Size": 664 + "res/anim-v21/design_bottom_sheet_slide_in.xml": { + "Size": 616 }, - "res/color-v23/design_tint_password_toggle.xml": { - "Size": 376 + "res/anim-v21/design_bottom_sheet_slide_out.xml": { + "Size": 616 }, "res/color/abc_background_cache_hint_selector_material_dark.xml": { "Size": 468 @@ -523,10 +490,10 @@ "res/color/mtrl_tabs_colored_ripple_color.xml": { "Size": 948 }, - "res/color/mtrl_tabs_icon_color_selector_colored.xml": { + "res/color/mtrl_tabs_icon_color_selector.xml": { "Size": 464 }, - "res/color/mtrl_tabs_icon_color_selector.xml": { + "res/color/mtrl_tabs_icon_color_selector_colored.xml": { "Size": 464 }, "res/color/mtrl_tabs_legacy_text_color_selector.xml": { @@ -544,159 +511,375 @@ "res/color/switch_thumb_material_light.xml": { "Size": 464 }, - "res/drawable-anydpi-v21/design_ic_visibility_off.xml": { - "Size": 1144 - }, - "res/drawable-anydpi-v21/design_ic_visibility.xml": { - "Size": 540 + "res/color-v21/abc_btn_colored_borderless_text_material.xml": { + "Size": 464 }, - "res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png": { - "Size": 272 + "res/color-v23/abc_btn_colored_borderless_text_material.xml": { + "Size": 500 }, - "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png": { - "Size": 227 + "res/color-v23/abc_btn_colored_text_material.xml": { + "Size": 500 }, - "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png": { - "Size": 404 + "res/color-v23/abc_color_highlight_material.xml": { + "Size": 544 }, - "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png": { - "Size": 464 + "res/color-v23/abc_tint_btn_checkable.xml": { + "Size": 624 }, - "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png": { - "Size": 563 + "res/color-v23/abc_tint_default.xml": { + "Size": 1120 }, - "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png": { - "Size": 1096 + "res/color-v23/abc_tint_edittext.xml": { + "Size": 668 }, - "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png": { - "Size": 1243 + "res/color-v23/abc_tint_seek_thumb.xml": { + "Size": 500 }, - "res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png": { - "Size": 226 + "res/color-v23/abc_tint_spinner.xml": { + "Size": 668 }, - "res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png": { - "Size": 171 + "res/color-v23/abc_tint_switch_track.xml": { + "Size": 664 }, - "res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png": { - "Size": 202 + "res/color-v23/design_tint_password_toggle.xml": { + "Size": 376 }, - "res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png": { - "Size": 404 + "res/drawable/abc_btn_borderless_material.xml": { + "Size": 588 }, - "res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png": { - "Size": 226 + "res/drawable/abc_btn_check_material.xml": { + "Size": 464 }, - "res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png": { - "Size": 215 + "res/drawable/abc_btn_check_material_anim.xml": { + "Size": 816 }, - "res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png": { - "Size": 389 + "res/drawable/abc_btn_colored_material.xml": { + "Size": 344 }, - "res/drawable-hdpi-v4/abc_ic_star_black_16dp.png": { - "Size": 263 + "res/drawable/abc_btn_default_mtrl_shape.xml": { + "Size": 932 }, - "res/drawable-hdpi-v4/abc_ic_star_black_36dp.png": { - "Size": 522 + "res/drawable/abc_btn_radio_material.xml": { + "Size": 464 }, - "res/drawable-hdpi-v4/abc_ic_star_black_48dp.png": { - "Size": 668 + "res/drawable/abc_btn_radio_material_anim.xml": { + "Size": 816 }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_16dp.png": { - "Size": 197 + "res/drawable/abc_cab_background_internal_bg.xml": { + "Size": 372 }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_36dp.png": { - "Size": 328 + "res/drawable/abc_cab_background_top_material.xml": { + "Size": 336 }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_48dp.png": { - "Size": 431 + "res/drawable/abc_dialog_material_background.xml": { + "Size": 716 }, - "res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png": { - "Size": 167 + "res/drawable/abc_edit_text_material.xml": { + "Size": 868 }, - "res/drawable-hdpi-v4/abc_list_focused_holo.9.png": { - "Size": 244 + "res/drawable/abc_ic_ab_back_material.xml": { + "Size": 692 }, - "res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png": { - "Size": 212 + "res/drawable/abc_ic_arrow_drop_right_black_24dp.xml": { + "Size": 1000 }, - "res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png": { - "Size": 208 + "res/drawable/abc_ic_clear_material.xml": { + "Size": 684 }, - "res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png": { - "Size": 208 + "res/drawable/abc_ic_go_search_api_material.xml": { + "Size": 640 }, - "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png": { - "Size": 228 + "res/drawable/abc_ic_menu_overflow_material.xml": { + "Size": 792 }, - "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png": { - "Size": 229 + "res/drawable/abc_ic_search_api_material.xml": { + "Size": 812 }, - "res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png": { - "Size": 738 + "res/drawable/abc_ic_voice_search_api_material.xml": { + "Size": 828 }, - "res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png": { - "Size": 1098 + "res/drawable/abc_item_background_holo_dark.xml": { + "Size": 1012 }, - "res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png": { - "Size": 201 + "res/drawable/abc_item_background_holo_light.xml": { + "Size": 1012 }, - "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png": { - "Size": 196 + "res/drawable/abc_list_divider_material.xml": { + "Size": 480 }, - "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png": { - "Size": 272 + "res/drawable/abc_list_selector_background_transition_holo_dark.xml": { + "Size": 424 }, - "res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png": { - "Size": 205 + "res/drawable/abc_list_selector_background_transition_holo_light.xml": { + "Size": 424 }, - "res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png": { - "Size": 196 + "res/drawable/abc_list_selector_holo_dark.xml": { + "Size": 1064 }, - "res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png": { - "Size": 345 + "res/drawable/abc_list_selector_holo_light.xml": { + "Size": 1064 }, - "res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png": { - "Size": 484 + "res/drawable/abc_ratingbar_indicator_material.xml": { + "Size": 664 }, - "res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png": { - "Size": 190 + "res/drawable/abc_ratingbar_material.xml": { + "Size": 664 }, - "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_dark.png": { - "Size": 278 + "res/drawable/abc_ratingbar_small_material.xml": { + "Size": 664 }, - "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_light.png": { - "Size": 278 + "res/drawable/abc_seekbar_thumb_material.xml": { + "Size": 1100 }, - "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_dark.png": { - "Size": 398 + "res/drawable/abc_seekbar_tick_mark_material.xml": { + "Size": 516 }, - "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_light.png": { - "Size": 396 + "res/drawable/abc_seekbar_track_material.xml": { + "Size": 1408 }, - "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_dark.png": { - "Size": 263 + "res/drawable/abc_spinner_textfield_background_material.xml": { + "Size": 1160 }, - "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_light.png": { - "Size": 262 + "res/drawable/abc_switch_thumb_material.xml": { + "Size": 464 }, - "res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png": { - "Size": 186 + "res/drawable/abc_tab_indicator_material.xml": { + "Size": 468 }, - "res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png": { - "Size": 192 + "res/drawable/abc_text_cursor_material.xml": { + "Size": 516 }, - "res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png": { - "Size": 178 + "res/drawable/abc_textfield_search_material.xml": { + "Size": 756 }, - "res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { + "res/drawable/abc_vector_test.xml": { + "Size": 612 + }, + "res/drawable/btn_checkbox_checked_mtrl.xml": { + "Size": 2688 + }, + "res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml": { + "Size": 688 + }, + "res/drawable/btn_checkbox_unchecked_mtrl.xml": { + "Size": 2660 + }, + "res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml": { + "Size": 688 + }, + "res/drawable/btn_radio_off_mtrl.xml": { + "Size": 1728 + }, + "res/drawable/btn_radio_off_to_on_mtrl_animation.xml": { + "Size": 680 + }, + "res/drawable/btn_radio_on_mtrl.xml": { + "Size": 1656 + }, + "res/drawable/btn_radio_on_to_off_mtrl_animation.xml": { + "Size": 680 + }, + "res/drawable/design_bottom_navigation_item_background.xml": { + "Size": 784 + }, + "res/drawable/design_fab_background.xml": { + "Size": 372 + }, + "res/drawable/design_password_eye.xml": { + "Size": 464 + }, + "res/drawable/design_snackbar_background.xml": { + "Size": 484 + }, + "res/drawable/ic_mtrl_chip_checked_black.xml": { + "Size": 600 + }, + "res/drawable/ic_mtrl_chip_checked_circle.xml": { + "Size": 940 + }, + "res/drawable/ic_mtrl_chip_close_circle.xml": { + "Size": 808 + }, + "res/drawable/mtrl_snackbar_background.xml": { + "Size": 484 + }, + "res/drawable/mtrl_tabs_default_indicator.xml": { + "Size": 628 + }, + "res/drawable/navigation_empty_icon.xml": { + "Size": 516 + }, + "res/drawable/notification_bg.xml": { + "Size": 532 + }, + "res/drawable/notification_bg_low.xml": { + "Size": 532 + }, + "res/drawable/notification_icon_background.xml": { + "Size": 372 + }, + "res/drawable/notification_tile_bg.xml": { + "Size": 304 + }, + "res/drawable/tooltip_frame_dark.xml": { + "Size": 484 + }, + "res/drawable/tooltip_frame_light.xml": { + "Size": 484 + }, + "res/drawable-anydpi-v21/design_ic_visibility.xml": { + "Size": 540 + }, + "res/drawable-anydpi-v21/design_ic_visibility_off.xml": { + "Size": 1144 + }, + "res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png": { + "Size": 272 + }, + "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png": { + "Size": 227 + }, + "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png": { + "Size": 404 + }, + "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png": { + "Size": 464 + }, + "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png": { + "Size": 563 + }, + "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png": { + "Size": 1096 + }, + "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png": { + "Size": 1243 + }, + "res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png": { + "Size": 226 + }, + "res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png": { + "Size": 171 + }, + "res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png": { + "Size": 202 + }, + "res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png": { + "Size": 404 + }, + "res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png": { + "Size": 226 + }, + "res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png": { + "Size": 215 + }, + "res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png": { + "Size": 389 + }, + "res/drawable-hdpi-v4/abc_ic_star_black_16dp.png": { + "Size": 263 + }, + "res/drawable-hdpi-v4/abc_ic_star_black_36dp.png": { + "Size": 522 + }, + "res/drawable-hdpi-v4/abc_ic_star_black_48dp.png": { + "Size": 668 + }, + "res/drawable-hdpi-v4/abc_ic_star_half_black_16dp.png": { + "Size": 197 + }, + "res/drawable-hdpi-v4/abc_ic_star_half_black_36dp.png": { + "Size": 328 + }, + "res/drawable-hdpi-v4/abc_ic_star_half_black_48dp.png": { + "Size": 431 + }, + "res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png": { + "Size": 167 + }, + "res/drawable-hdpi-v4/abc_list_focused_holo.9.png": { + "Size": 244 + }, + "res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png": { + "Size": 212 + }, + "res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png": { + "Size": 208 + }, + "res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png": { + "Size": 208 + }, + "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png": { + "Size": 228 + }, + "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png": { + "Size": 229 + }, + "res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png": { + "Size": 738 + }, + "res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png": { + "Size": 1098 + }, + "res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png": { + "Size": 201 + }, + "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png": { + "Size": 196 + }, + "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png": { + "Size": 272 + }, + "res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png": { + "Size": 205 + }, + "res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png": { + "Size": 196 + }, + "res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png": { + "Size": 345 + }, + "res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png": { + "Size": 484 + }, + "res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png": { + "Size": 190 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_dark.png": { + "Size": 278 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_light.png": { + "Size": 278 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_dark.png": { + "Size": 398 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_light.png": { + "Size": 396 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_dark.png": { + "Size": 263 + }, + "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_light.png": { + "Size": 262 + }, + "res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png": { + "Size": 186 + }, + "res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png": { + "Size": 192 + }, + "res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png": { "Size": 178 }, - "res/drawable-hdpi-v4/design_ic_visibility_off.png": { - "Size": 507 + "res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { + "Size": 178 }, "res/drawable-hdpi-v4/design_ic_visibility.png": { "Size": 470 }, + "res/drawable-hdpi-v4/design_ic_visibility_off.png": { + "Size": 507 + }, "res/drawable-hdpi-v4/icon.png": { "Size": 4762 }, @@ -706,12 +889,12 @@ "res/drawable-hdpi-v4/notification_bg_low_pressed.9.png": { "Size": 225 }, - "res/drawable-hdpi-v4/notification_bg_normal_pressed.9.png": { - "Size": 225 - }, "res/drawable-hdpi-v4/notification_bg_normal.9.png": { "Size": 212 }, + "res/drawable-hdpi-v4/notification_bg_normal_pressed.9.png": { + "Size": 225 + }, "res/drawable-hdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 107 }, @@ -901,12 +1084,12 @@ "res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 178 }, - "res/drawable-mdpi-v4/design_ic_visibility_off.png": { - "Size": 351 - }, "res/drawable-mdpi-v4/design_ic_visibility.png": { "Size": 309 }, + "res/drawable-mdpi-v4/design_ic_visibility_off.png": { + "Size": 351 + }, "res/drawable-mdpi-v4/icon.png": { "Size": 2200 }, @@ -916,12 +1099,12 @@ "res/drawable-mdpi-v4/notification_bg_low_pressed.9.png": { "Size": 223 }, - "res/drawable-mdpi-v4/notification_bg_normal_pressed.9.png": { - "Size": 223 - }, "res/drawable-mdpi-v4/notification_bg_normal.9.png": { "Size": 215 }, + "res/drawable-mdpi-v4/notification_bg_normal_pressed.9.png": { + "Size": 223 + }, "res/drawable-mdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 98 }, @@ -1129,12 +1312,12 @@ "res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 182 }, - "res/drawable-xhdpi-v4/design_ic_visibility_off.png": { - "Size": 629 - }, "res/drawable-xhdpi-v4/design_ic_visibility.png": { "Size": 593 }, + "res/drawable-xhdpi-v4/design_ic_visibility_off.png": { + "Size": 629 + }, "res/drawable-xhdpi-v4/icon.png": { "Size": 7462 }, @@ -1144,12 +1327,12 @@ "res/drawable-xhdpi-v4/notification_bg_low_pressed.9.png": { "Size": 252 }, - "res/drawable-xhdpi-v4/notification_bg_normal_pressed.9.png": { - "Size": 247 - }, "res/drawable-xhdpi-v4/notification_bg_normal.9.png": { "Size": 221 }, + "res/drawable-xhdpi-v4/notification_bg_normal_pressed.9.png": { + "Size": 247 + }, "res/drawable-xhdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 138 }, @@ -1294,12 +1477,12 @@ "res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 186 }, - "res/drawable-xxhdpi-v4/design_ic_visibility_off.png": { - "Size": 884 - }, "res/drawable-xxhdpi-v4/design_ic_visibility.png": { "Size": 868 }, + "res/drawable-xxhdpi-v4/design_ic_visibility_off.png": { + "Size": 884 + }, "res/drawable-xxhdpi-v4/icon.png": { "Size": 13092 }, @@ -1381,207 +1564,15 @@ "res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_light.png": { "Size": 513 }, - "res/drawable-xxxhdpi-v4/design_ic_visibility_off.png": { - "Size": 1201 - }, "res/drawable-xxxhdpi-v4/design_ic_visibility.png": { "Size": 1155 }, + "res/drawable-xxxhdpi-v4/design_ic_visibility_off.png": { + "Size": 1201 + }, "res/drawable-xxxhdpi-v4/icon.png": { "Size": 20118 }, - "res/drawable/abc_btn_borderless_material.xml": { - "Size": 588 - }, - "res/drawable/abc_btn_check_material_anim.xml": { - "Size": 816 - }, - "res/drawable/abc_btn_check_material.xml": { - "Size": 464 - }, - "res/drawable/abc_btn_colored_material.xml": { - "Size": 344 - }, - "res/drawable/abc_btn_default_mtrl_shape.xml": { - "Size": 932 - }, - "res/drawable/abc_btn_radio_material_anim.xml": { - "Size": 816 - }, - "res/drawable/abc_btn_radio_material.xml": { - "Size": 464 - }, - "res/drawable/abc_cab_background_internal_bg.xml": { - "Size": 372 - }, - "res/drawable/abc_cab_background_top_material.xml": { - "Size": 336 - }, - "res/drawable/abc_dialog_material_background.xml": { - "Size": 716 - }, - "res/drawable/abc_edit_text_material.xml": { - "Size": 868 - }, - "res/drawable/abc_ic_ab_back_material.xml": { - "Size": 692 - }, - "res/drawable/abc_ic_arrow_drop_right_black_24dp.xml": { - "Size": 1000 - }, - "res/drawable/abc_ic_clear_material.xml": { - "Size": 684 - }, - "res/drawable/abc_ic_go_search_api_material.xml": { - "Size": 640 - }, - "res/drawable/abc_ic_menu_overflow_material.xml": { - "Size": 792 - }, - "res/drawable/abc_ic_search_api_material.xml": { - "Size": 812 - }, - "res/drawable/abc_ic_voice_search_api_material.xml": { - "Size": 828 - }, - "res/drawable/abc_item_background_holo_dark.xml": { - "Size": 1012 - }, - "res/drawable/abc_item_background_holo_light.xml": { - "Size": 1012 - }, - "res/drawable/abc_list_divider_material.xml": { - "Size": 480 - }, - "res/drawable/abc_list_selector_background_transition_holo_dark.xml": { - "Size": 424 - }, - "res/drawable/abc_list_selector_background_transition_holo_light.xml": { - "Size": 424 - }, - "res/drawable/abc_list_selector_holo_dark.xml": { - "Size": 1064 - }, - "res/drawable/abc_list_selector_holo_light.xml": { - "Size": 1064 - }, - "res/drawable/abc_ratingbar_indicator_material.xml": { - "Size": 664 - }, - "res/drawable/abc_ratingbar_material.xml": { - "Size": 664 - }, - "res/drawable/abc_ratingbar_small_material.xml": { - "Size": 664 - }, - "res/drawable/abc_seekbar_thumb_material.xml": { - "Size": 1100 - }, - "res/drawable/abc_seekbar_tick_mark_material.xml": { - "Size": 516 - }, - "res/drawable/abc_seekbar_track_material.xml": { - "Size": 1408 - }, - "res/drawable/abc_spinner_textfield_background_material.xml": { - "Size": 1160 - }, - "res/drawable/abc_switch_thumb_material.xml": { - "Size": 464 - }, - "res/drawable/abc_tab_indicator_material.xml": { - "Size": 468 - }, - "res/drawable/abc_text_cursor_material.xml": { - "Size": 516 - }, - "res/drawable/abc_textfield_search_material.xml": { - "Size": 756 - }, - "res/drawable/abc_vector_test.xml": { - "Size": 612 - }, - "res/drawable/btn_checkbox_checked_mtrl.xml": { - "Size": 2688 - }, - "res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml": { - "Size": 688 - }, - "res/drawable/btn_checkbox_unchecked_mtrl.xml": { - "Size": 2660 - }, - "res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml": { - "Size": 688 - }, - "res/drawable/btn_radio_off_mtrl.xml": { - "Size": 1728 - }, - "res/drawable/btn_radio_off_to_on_mtrl_animation.xml": { - "Size": 680 - }, - "res/drawable/btn_radio_on_mtrl.xml": { - "Size": 1656 - }, - "res/drawable/btn_radio_on_to_off_mtrl_animation.xml": { - "Size": 680 - }, - "res/drawable/design_bottom_navigation_item_background.xml": { - "Size": 784 - }, - "res/drawable/design_fab_background.xml": { - "Size": 372 - }, - "res/drawable/design_password_eye.xml": { - "Size": 464 - }, - "res/drawable/design_snackbar_background.xml": { - "Size": 484 - }, - "res/drawable/ic_mtrl_chip_checked_black.xml": { - "Size": 600 - }, - "res/drawable/ic_mtrl_chip_checked_circle.xml": { - "Size": 940 - }, - "res/drawable/ic_mtrl_chip_close_circle.xml": { - "Size": 808 - }, - "res/drawable/mtrl_snackbar_background.xml": { - "Size": 484 - }, - "res/drawable/mtrl_tabs_default_indicator.xml": { - "Size": 628 - }, - "res/drawable/navigation_empty_icon.xml": { - "Size": 516 - }, - "res/drawable/notification_bg_low.xml": { - "Size": 532 - }, - "res/drawable/notification_bg.xml": { - "Size": 532 - }, - "res/drawable/notification_icon_background.xml": { - "Size": 372 - }, - "res/drawable/notification_tile_bg.xml": { - "Size": 304 - }, - "res/drawable/tooltip_frame_dark.xml": { - "Size": 484 - }, - "res/drawable/tooltip_frame_light.xml": { - "Size": 484 - }, - "res/interpolator-v21/mtrl_fast_out_linear_in.xml": { - "Size": 400 - }, - "res/interpolator-v21/mtrl_fast_out_slow_in.xml": { - "Size": 400 - }, - "res/interpolator-v21/mtrl_linear_out_slow_in.xml": { - "Size": 400 - }, "res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0.xml": { "Size": 316 }, @@ -1609,53 +1600,20 @@ "res/interpolator/mtrl_fast_out_slow_in.xml": { "Size": 144 }, - "res/interpolator/mtrl_linear_out_slow_in.xml": { - "Size": 136 - }, "res/interpolator/mtrl_linear.xml": { "Size": 132 }, - "res/layout-sw600dp-v13/design_layout_snackbar.xml": { - "Size": 528 - }, - "res/layout-sw600dp-v13/mtrl_layout_snackbar.xml": { - "Size": 528 - }, - "res/layout-v16/notification_template_custom_big.xml": { - "Size": 3208 - }, - "res/layout-v21/abc_screen_toolbar.xml": { - "Size": 1504 - }, - "res/layout-v21/fallbacktoolbardonotuse.xml": { - "Size": 496 - }, - "res/layout-v21/notification_action_tombstone.xml": { - "Size": 1228 - }, - "res/layout-v21/notification_action.xml": { - "Size": 1052 - }, - "res/layout-v21/notification_template_custom_big.xml": { - "Size": 2456 - }, - "res/layout-v21/notification_template_icon_group.xml": { - "Size": 988 - }, - "res/layout-v21/toolbar.xml": { - "Size": 496 - }, - "res/layout-v22/abc_alert_dialog_button_bar_material.xml": { - "Size": 1584 + "res/interpolator/mtrl_linear_out_slow_in.xml": { + "Size": 136 }, - "res/layout-v26/abc_screen_toolbar.xml": { - "Size": 1560 + "res/interpolator-v21/mtrl_fast_out_linear_in.xml": { + "Size": 400 }, - "res/layout-watch-v20/abc_alert_dialog_button_bar_material.xml": { - "Size": 1208 + "res/interpolator-v21/mtrl_fast_out_slow_in.xml": { + "Size": 400 }, - "res/layout-watch-v20/abc_alert_dialog_title_material.xml": { - "Size": 1352 + "res/interpolator-v21/mtrl_linear_out_slow_in.xml": { + "Size": 400 }, "res/layout/abc_action_bar_title_item.xml": { "Size": 872 @@ -1675,12 +1633,12 @@ "res/layout/abc_action_mode_close_item_material.xml": { "Size": 840 }, - "res/layout/abc_activity_chooser_view_list_item.xml": { - "Size": 1304 - }, "res/layout/abc_activity_chooser_view.xml": { "Size": 1684 }, + "res/layout/abc_activity_chooser_view_list_item.xml": { + "Size": 1304 + }, "res/layout/abc_alert_dialog_button_bar_material.xml": { "Size": 1536 }, @@ -1720,12 +1678,12 @@ "res/layout/abc_screen_content_include.xml": { "Size": 548 }, - "res/layout/abc_screen_simple_overlay_action_mode.xml": { - "Size": 792 - }, "res/layout/abc_screen_simple.xml": { "Size": 832 }, + "res/layout/abc_screen_simple_overlay_action_mode.xml": { + "Size": 792 + }, "res/layout/abc_screen_toolbar.xml": { "Size": 1452 }, @@ -1759,12 +1717,12 @@ "res/layout/design_bottom_sheet_dialog.xml": { "Size": 1184 }, - "res/layout/design_layout_snackbar_include.xml": { - "Size": 1444 - }, "res/layout/design_layout_snackbar.xml": { "Size": 528 }, + "res/layout/design_layout_snackbar_include.xml": { + "Size": 1444 + }, "res/layout/design_layout_tab_icon.xml": { "Size": 408 }, @@ -1774,6 +1732,9 @@ "res/layout/design_menu_item_action_area.xml": { "Size": 320 }, + "res/layout/design_navigation_item.xml": { + "Size": 536 + }, "res/layout/design_navigation_item_header.xml": { "Size": 440 }, @@ -1783,15 +1744,12 @@ "res/layout/design_navigation_item_subheader.xml": { "Size": 564 }, - "res/layout/design_navigation_item.xml": { - "Size": 536 + "res/layout/design_navigation_menu.xml": { + "Size": 528 }, "res/layout/design_navigation_menu_item.xml": { "Size": 856 }, - "res/layout/design_navigation_menu.xml": { - "Size": 528 - }, "res/layout/design_text_input_password_icon.xml": { "Size": 564 }, @@ -1807,35 +1765,35 @@ "res/layout/main.xml": { "Size": 544 }, - "res/layout/mtrl_layout_snackbar_include.xml": { - "Size": 1404 - }, "res/layout/mtrl_layout_snackbar.xml": { "Size": 528 }, - "res/layout/notification_action_tombstone.xml": { - "Size": 1332 + "res/layout/mtrl_layout_snackbar_include.xml": { + "Size": 1404 }, "res/layout/notification_action.xml": { "Size": 1156 }, + "res/layout/notification_action_tombstone.xml": { + "Size": 1332 + }, "res/layout/notification_media_action.xml": { "Size": 564 }, "res/layout/notification_media_cancel_action.xml": { "Size": 744 }, + "res/layout/notification_template_big_media.xml": { + "Size": 1696 + }, "res/layout/notification_template_big_media_custom.xml": { "Size": 3044 }, - "res/layout/notification_template_big_media_narrow_custom.xml": { - "Size": 3216 - }, "res/layout/notification_template_big_media_narrow.xml": { "Size": 1824 }, - "res/layout/notification_template_big_media.xml": { - "Size": 1696 + "res/layout/notification_template_big_media_narrow_custom.xml": { + "Size": 3216 }, "res/layout/notification_template_icon_group.xml": { "Size": 392 @@ -1843,12 +1801,12 @@ "res/layout/notification_template_lines_media.xml": { "Size": 2872 }, - "res/layout/notification_template_media_custom.xml": { - "Size": 2756 - }, "res/layout/notification_template_media.xml": { "Size": 1292 }, + "res/layout/notification_template_media_custom.xml": { + "Size": 2756 + }, "res/layout/notification_template_part_chronometer.xml": { "Size": 440 }, @@ -1879,9 +1837,51 @@ "res/layout/toolbar.xml": { "Size": 452 }, + "res/layout-sw600dp-v13/design_layout_snackbar.xml": { + "Size": 528 + }, + "res/layout-sw600dp-v13/mtrl_layout_snackbar.xml": { + "Size": 528 + }, + "res/layout-v16/notification_template_custom_big.xml": { + "Size": 3208 + }, + "res/layout-v21/abc_screen_toolbar.xml": { + "Size": 1504 + }, + "res/layout-v21/fallbacktoolbardonotuse.xml": { + "Size": 496 + }, + "res/layout-v21/notification_action.xml": { + "Size": 1052 + }, + "res/layout-v21/notification_action_tombstone.xml": { + "Size": 1228 + }, + "res/layout-v21/notification_template_custom_big.xml": { + "Size": 2456 + }, + "res/layout-v21/notification_template_icon_group.xml": { + "Size": 988 + }, + "res/layout-v21/toolbar.xml": { + "Size": 496 + }, + "res/layout-v22/abc_alert_dialog_button_bar_material.xml": { + "Size": 1584 + }, + "res/layout-v26/abc_screen_toolbar.xml": { + "Size": 1560 + }, + "res/layout-watch-v20/abc_alert_dialog_button_bar_material.xml": { + "Size": 1208 + }, + "res/layout-watch-v20/abc_alert_dialog_title_material.xml": { + "Size": 1352 + }, "resources.arsc": { "Size": 341040 } }, - "PackageSize": 9570462 + "PackageSize": 9566366 } \ No newline at end of file From 2b259af340533dc1811f4cd06bfe2d65ba8b2726 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 2 Jun 2022 14:59:09 +0200 Subject: [PATCH 04/33] Add expected assemblies --- .../Tests/Xamarin.Android.Build.Tests/PackagingTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index a797e2baa77..308ce39cd21 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -89,8 +89,15 @@ public void CheckIncludedAssemblies ([Values (false, true)] bool usesAssemblySto "Mono.Android.dll", "rc.bin", "System.Console.dll", + "System.Formats.Asn1.dll", + "System.IO.Compression.dll", + "System.Net.Http.dll", + "System.Net.NameResolution.dll", + "System.Net.Primitives.dll", + "System.Net.Requests.dll", "System.Private.CoreLib.dll", "System.Runtime.dll", + "System.Runtime.Numerics.dll", "System.Linq.dll", "UnnamedProject.dll", } : From 6f324fb0ddbe01e98549d97e6a324a70b938d0ca Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jun 2022 10:09:48 +0200 Subject: [PATCH 05/33] Revert "Add expected assemblies" This reverts commit 2b259af340533dc1811f4cd06bfe2d65ba8b2726. --- .../Tests/Xamarin.Android.Build.Tests/PackagingTest.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index 308ce39cd21..a797e2baa77 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -89,15 +89,8 @@ public void CheckIncludedAssemblies ([Values (false, true)] bool usesAssemblySto "Mono.Android.dll", "rc.bin", "System.Console.dll", - "System.Formats.Asn1.dll", - "System.IO.Compression.dll", - "System.Net.Http.dll", - "System.Net.NameResolution.dll", - "System.Net.Primitives.dll", - "System.Net.Requests.dll", "System.Private.CoreLib.dll", "System.Runtime.dll", - "System.Runtime.Numerics.dll", "System.Linq.dll", "UnnamedProject.dll", } : From e683125aad0c6d464c19636aa3cda89eaa948d9e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jun 2022 10:10:02 +0200 Subject: [PATCH 06/33] Revert "Update apkdesc files" This reverts commit 449151eaf74a885127f9088df1f86bc9aa9161e7. --- .../BuildReleaseArm64SimpleDotNet.apkdesc | 64 +- .../BuildReleaseArm64SimpleLegacy.apkdesc | 26 +- .../BuildReleaseArm64XFormsDotNet.apkdesc | 37 +- .../BuildReleaseArm64XFormsLegacy.apkdesc | 934 +++++++++--------- 4 files changed, 505 insertions(+), 556 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc index f65d32556e0..0ead66e3aa6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc @@ -5,76 +5,34 @@ "Size": 3032 }, "assemblies/Java.Interop.dll": { - "Size": 60761 + "Size": 59377 }, "assemblies/Mono.Android.dll": { - "Size": 156719 + "Size": 89098 }, "assemblies/rc.bin": { "Size": 1083 }, - "assemblies/System.Collections.Concurrent.dll": { - "Size": 9171 - }, - "assemblies/System.Collections.dll": { - "Size": 4282 - }, - "assemblies/System.Collections.NonGeneric.dll": { - "Size": 6236 - }, "assemblies/System.Console.dll": { - "Size": 6506 - }, - "assemblies/System.Formats.Asn1.dll": { - "Size": 17426 - }, - "assemblies/System.IO.Compression.dll": { - "Size": 16266 + "Size": 6475 }, "assemblies/System.Linq.dll": { - "Size": 10316 - }, - "assemblies/System.Net.Http.dll": { - "Size": 75341 - }, - "assemblies/System.Net.NameResolution.dll": { - "Size": 12592 - }, - "assemblies/System.Net.Primitives.dll": { - "Size": 32401 - }, - "assemblies/System.Net.Requests.dll": { - "Size": 3519 + "Size": 9971 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 621008 - }, - "assemblies/System.Private.Uri.dll": { - "Size": 37777 - }, - "assemblies/System.Private.Xml.dll": { - "Size": 140803 + "Size": 524631 }, "assemblies/System.Runtime.dll": { "Size": 2410 }, - "assemblies/System.Runtime.Numerics.dll": { - "Size": 23826 - }, - "assemblies/System.Security.Cryptography.dll": { - "Size": 9671 - }, - "assemblies/System.Text.RegularExpressions.dll": { - "Size": 9593 - }, "assemblies/UnnamedProject.dll": { - "Size": 3550 + "Size": 3553 }, "classes.dex": { "Size": 347544 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 484128 + "Size": 512552 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667768 @@ -89,16 +47,16 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 16208 + "Size": 9488 }, "META-INF/BNDLTOOL.RSA": { "Size": 1213 }, "META-INF/BNDLTOOL.SF": { - "Size": 4101 + "Size": 2570 }, "META-INF/MANIFEST.MF": { - "Size": 3974 + "Size": 2443 }, "res/drawable-hdpi-v4/icon.png": { "Size": 4791 @@ -125,5 +83,5 @@ "Size": 1904 } }, - "PackageSize": 3583086 + "PackageSize": 3020767 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc index 8830d536242..a0a7e65caa1 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc @@ -5,43 +5,43 @@ "Size": 2604 }, "assemblies/Java.Interop.dll": { - "Size": 68893 + "Size": 67950 }, "assemblies/Mono.Android.dll": { - "Size": 256736 + "Size": 257187 }, "assemblies/mscorlib.dll": { - "Size": 769010 + "Size": 769016 }, "assemblies/System.Core.dll": { - "Size": 28189 + "Size": 28198 }, "assemblies/System.dll": { - "Size": 9178 + "Size": 9180 }, "assemblies/UnnamedProject.dll": { - "Size": 2870 + "Size": 2879 }, "classes.dex": { - "Size": 350636 + "Size": 349736 }, "lib/arm64-v8a/libmono-btls-shared.so": { "Size": 1613872 }, - "lib/arm64-v8a/libmonodroid.so": { - "Size": 394504 - }, "lib/arm64-v8a/libmono-native.so": { "Size": 750976 }, + "lib/arm64-v8a/libmonodroid.so": { + "Size": 421872 + }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 4051864 + "Size": 4030448 }, "lib/arm64-v8a/libxa-internal-api.so": { "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 19864 + "Size": 19872 }, "META-INF/ANDROIDD.RSA": { "Size": 1213 @@ -74,5 +74,5 @@ "Size": 1724 } }, - "PackageSize": 4048596 + "PackageSize": 4052692 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc index 32b8ec89b7f..96a6684a9b7 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc @@ -8,10 +8,10 @@ "Size": 7114 }, "assemblies/Java.Interop.dll": { - "Size": 66817 + "Size": 66738 }, "assemblies/Mono.Android.dll": { - "Size": 451768 + "Size": 444811 }, "assemblies/mscorlib.dll": { "Size": 3891 @@ -26,7 +26,7 @@ "Size": 10777 }, "assemblies/System.Collections.dll": { - "Size": 16213 + "Size": 16153 }, "assemblies/System.Collections.NonGeneric.dll": { "Size": 7932 @@ -58,9 +58,6 @@ "assemblies/System.Drawing.Primitives.dll": { "Size": 12123 }, - "assemblies/System.Formats.Asn1.dll": { - "Size": 17426 - }, "assemblies/System.IO.Compression.dll": { "Size": 16741 }, @@ -74,13 +71,10 @@ "Size": 177473 }, "assemblies/System.Net.Http.dll": { - "Size": 75831 - }, - "assemblies/System.Net.NameResolution.dll": { - "Size": 12592 + "Size": 65645 }, "assemblies/System.Net.Primitives.dll": { - "Size": 33309 + "Size": 22070 }, "assemblies/System.Net.Requests.dll": { "Size": 3660 @@ -89,10 +83,10 @@ "Size": 8540 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 802808 + "Size": 800901 }, "assemblies/System.Private.DataContractSerialization.dll": { - "Size": 185395 + "Size": 185328 }, "assemblies/System.Private.Uri.dll": { "Size": 42655 @@ -106,9 +100,6 @@ "assemblies/System.Runtime.dll": { "Size": 2603 }, - "assemblies/System.Runtime.Numerics.dll": { - "Size": 33362 - }, "assemblies/System.Runtime.Serialization.dll": { "Size": 2020 }, @@ -119,7 +110,7 @@ "Size": 3834 }, "assemblies/System.Security.Cryptography.dll": { - "Size": 10020 + "Size": 7751 }, "assemblies/System.Text.RegularExpressions.dll": { "Size": 159573 @@ -128,7 +119,7 @@ "Size": 1910 }, "assemblies/UnnamedProject.dll": { - "Size": 117241 + "Size": 117243 }, "assemblies/Xamarin.AndroidX.Activity.dll": { "Size": 5941 @@ -200,7 +191,7 @@ "Size": 3460156 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 484128 + "Size": 512552 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667768 @@ -215,7 +206,7 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 98744 + "Size": 98592 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -329,13 +320,13 @@ "Size": 1213 }, "META-INF/BNDLTOOL.SF": { - "Size": 79085 + "Size": 78757 }, "META-INF/com.google.android.material_material.version": { "Size": 10 }, "META-INF/MANIFEST.MF": { - "Size": 78958 + "Size": 78630 }, "META-INF/proguard/androidx-annotations.pro": { "Size": 339 @@ -1970,5 +1961,5 @@ "Size": 341228 } }, - "PackageSize": 8442439 + "PackageSize": 8347981 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc index 8b15f83e368..691f59a584e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc @@ -5,133 +5,133 @@ "Size": 3140 }, "assemblies/FormsViewGroup.dll": { - "Size": 7207 + "Size": 7215 }, "assemblies/Java.Interop.dll": { - "Size": 69916 + "Size": 68916 }, "assemblies/Mono.Android.dll": { - "Size": 567807 + "Size": 567711 }, "assemblies/Mono.Security.dll": { - "Size": 68429 + "Size": 68432 }, "assemblies/mscorlib.dll": { - "Size": 915392 + "Size": 915405 }, "assemblies/System.Core.dll": { - "Size": 164045 + "Size": 164046 }, "assemblies/System.dll": { - "Size": 388861 + "Size": 388864 }, "assemblies/System.Drawing.Common.dll": { - "Size": 12356 + "Size": 12365 }, "assemblies/System.Net.Http.dll": { - "Size": 110693 + "Size": 110642 }, "assemblies/System.Numerics.dll": { - "Size": 15681 + "Size": 15683 }, "assemblies/System.Runtime.Serialization.dll": { - "Size": 186653 + "Size": 186660 }, "assemblies/System.ServiceModel.Internals.dll": { - "Size": 26585 + "Size": 26593 }, "assemblies/System.Xml.dll": { - "Size": 395651 + "Size": 395657 }, "assemblies/UnnamedProject.dll": { - "Size": 116888 + "Size": 116898 }, "assemblies/Xamarin.AndroidX.Activity.dll": { - "Size": 7689 + "Size": 7697 }, "assemblies/Xamarin.AndroidX.AppCompat.AppCompatResources.dll": { - "Size": 6640 + "Size": 6648 }, "assemblies/Xamarin.AndroidX.AppCompat.dll": { - "Size": 125325 + "Size": 125328 }, "assemblies/Xamarin.AndroidX.CardView.dll": { - "Size": 7357 + "Size": 7367 }, "assemblies/Xamarin.AndroidX.CoordinatorLayout.dll": { - "Size": 18264 + "Size": 18272 }, "assemblies/Xamarin.AndroidX.Core.dll": { - "Size": 131924 + "Size": 131930 }, "assemblies/Xamarin.AndroidX.DrawerLayout.dll": { - "Size": 15422 + "Size": 15425 }, "assemblies/Xamarin.AndroidX.Fragment.dll": { - "Size": 43130 + "Size": 43134 }, "assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": { - "Size": 6708 + "Size": 6714 }, "assemblies/Xamarin.AndroidX.Lifecycle.Common.dll": { - "Size": 7055 + "Size": 7062 }, "assemblies/Xamarin.AndroidX.Lifecycle.LiveData.Core.dll": { - "Size": 7185 + "Size": 7194 }, "assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": { - "Size": 4862 + "Size": 4873 }, "assemblies/Xamarin.AndroidX.Loader.dll": { - "Size": 13578 + "Size": 13585 }, "assemblies/Xamarin.AndroidX.RecyclerView.dll": { - "Size": 102322 + "Size": 102327 }, "assemblies/Xamarin.AndroidX.SavedState.dll": { - "Size": 6265 + "Size": 6268 }, "assemblies/Xamarin.AndroidX.SwipeRefreshLayout.dll": { - "Size": 11261 + "Size": 11272 }, "assemblies/Xamarin.AndroidX.ViewPager.dll": { - "Size": 19416 + "Size": 19424 }, "assemblies/Xamarin.Forms.Core.dll": { - "Size": 524728 + "Size": 524736 }, "assemblies/Xamarin.Forms.Platform.Android.dll": { - "Size": 384861 + "Size": 384872 }, "assemblies/Xamarin.Forms.Platform.dll": { "Size": 56878 }, "assemblies/Xamarin.Forms.Xaml.dll": { - "Size": 55795 + "Size": 55801 }, "assemblies/Xamarin.Google.Android.Material.dll": { - "Size": 43489 + "Size": 43497 }, "classes.dex": { - "Size": 3462744 + "Size": 3462080 }, "lib/arm64-v8a/libmono-btls-shared.so": { "Size": 1613872 }, - "lib/arm64-v8a/libmonodroid.so": { - "Size": 394504 - }, "lib/arm64-v8a/libmono-native.so": { "Size": 750976 }, + "lib/arm64-v8a/libmonodroid.so": { + "Size": 421872 + }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 4051864 + "Size": 4030448 }, "lib/arm64-v8a/libxa-internal-api.so": { "Size": 65512 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 104920 + "Size": 104928 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -145,10 +145,10 @@ "META-INF/androidx.activity_activity.version": { "Size": 6 }, - "META-INF/androidx.appcompat_appcompat.version": { + "META-INF/androidx.appcompat_appcompat-resources.version": { "Size": 6 }, - "META-INF/androidx.appcompat_appcompat-resources.version": { + "META-INF/androidx.appcompat_appcompat.version": { "Size": 6 }, "META-INF/androidx.arch.core_core-runtime.version": { @@ -196,10 +196,10 @@ "META-INF/androidx.legacy_legacy-support-v4.version": { "Size": 6 }, - "META-INF/androidx.lifecycle_lifecycle-livedata.version": { + "META-INF/androidx.lifecycle_lifecycle-livedata-core.version": { "Size": 6 }, - "META-INF/androidx.lifecycle_lifecycle-livedata-core.version": { + "META-INF/androidx.lifecycle_lifecycle-livedata.version": { "Size": 6 }, "META-INF/androidx.lifecycle_lifecycle-runtime.version": { @@ -235,10 +235,10 @@ "META-INF/androidx.transition_transition.version": { "Size": 6 }, - "META-INF/androidx.vectordrawable_vectordrawable.version": { + "META-INF/androidx.vectordrawable_vectordrawable-animated.version": { "Size": 6 }, - "META-INF/androidx.vectordrawable_vectordrawable-animated.version": { + "META-INF/androidx.vectordrawable_vectordrawable.version": { "Size": 6 }, "META-INF/androidx.versionedparcelable_versionedparcelable.version": { @@ -256,6 +256,12 @@ "META-INF/proguard/androidx-annotations.pro": { "Size": 339 }, + "res/anim-v21/design_bottom_sheet_slide_in.xml": { + "Size": 616 + }, + "res/anim-v21/design_bottom_sheet_slide_out.xml": { + "Size": 616 + }, "res/anim/abc_fade_in.xml": { "Size": 388 }, @@ -352,6 +358,9 @@ "res/anim/exittoright.xml": { "Size": 468 }, + "res/animator-v21/design_appbar_state_list_animator.xml": { + "Size": 1216 + }, "res/animator/design_fab_hide_motion_spec.xml": { "Size": 796 }, @@ -379,14 +388,38 @@ "res/animator/mtrl_fab_transformation_sheet_expand_spec.xml": { "Size": 1888 }, - "res/animator-v21/design_appbar_state_list_animator.xml": { - "Size": 1216 + "res/color-v21/abc_btn_colored_borderless_text_material.xml": { + "Size": 464 }, - "res/anim-v21/design_bottom_sheet_slide_in.xml": { - "Size": 616 + "res/color-v23/abc_btn_colored_borderless_text_material.xml": { + "Size": 500 }, - "res/anim-v21/design_bottom_sheet_slide_out.xml": { - "Size": 616 + "res/color-v23/abc_btn_colored_text_material.xml": { + "Size": 500 + }, + "res/color-v23/abc_color_highlight_material.xml": { + "Size": 544 + }, + "res/color-v23/abc_tint_btn_checkable.xml": { + "Size": 624 + }, + "res/color-v23/abc_tint_default.xml": { + "Size": 1120 + }, + "res/color-v23/abc_tint_edittext.xml": { + "Size": 668 + }, + "res/color-v23/abc_tint_seek_thumb.xml": { + "Size": 500 + }, + "res/color-v23/abc_tint_spinner.xml": { + "Size": 668 + }, + "res/color-v23/abc_tint_switch_track.xml": { + "Size": 664 + }, + "res/color-v23/design_tint_password_toggle.xml": { + "Size": 376 }, "res/color/abc_background_cache_hint_selector_material_dark.xml": { "Size": 468 @@ -490,10 +523,10 @@ "res/color/mtrl_tabs_colored_ripple_color.xml": { "Size": 948 }, - "res/color/mtrl_tabs_icon_color_selector.xml": { + "res/color/mtrl_tabs_icon_color_selector_colored.xml": { "Size": 464 }, - "res/color/mtrl_tabs_icon_color_selector_colored.xml": { + "res/color/mtrl_tabs_icon_color_selector.xml": { "Size": 464 }, "res/color/mtrl_tabs_legacy_text_color_selector.xml": { @@ -511,362 +544,146 @@ "res/color/switch_thumb_material_light.xml": { "Size": 464 }, - "res/color-v21/abc_btn_colored_borderless_text_material.xml": { - "Size": 464 - }, - "res/color-v23/abc_btn_colored_borderless_text_material.xml": { - "Size": 500 + "res/drawable-anydpi-v21/design_ic_visibility_off.xml": { + "Size": 1144 }, - "res/color-v23/abc_btn_colored_text_material.xml": { - "Size": 500 + "res/drawable-anydpi-v21/design_ic_visibility.xml": { + "Size": 540 }, - "res/color-v23/abc_color_highlight_material.xml": { - "Size": 544 + "res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png": { + "Size": 272 }, - "res/color-v23/abc_tint_btn_checkable.xml": { - "Size": 624 + "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png": { + "Size": 227 }, - "res/color-v23/abc_tint_default.xml": { - "Size": 1120 + "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png": { + "Size": 404 }, - "res/color-v23/abc_tint_edittext.xml": { - "Size": 668 + "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png": { + "Size": 464 }, - "res/color-v23/abc_tint_seek_thumb.xml": { - "Size": 500 + "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png": { + "Size": 563 }, - "res/color-v23/abc_tint_spinner.xml": { - "Size": 668 + "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png": { + "Size": 1096 }, - "res/color-v23/abc_tint_switch_track.xml": { - "Size": 664 + "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png": { + "Size": 1243 }, - "res/color-v23/design_tint_password_toggle.xml": { - "Size": 376 + "res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png": { + "Size": 226 }, - "res/drawable/abc_btn_borderless_material.xml": { - "Size": 588 + "res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png": { + "Size": 171 }, - "res/drawable/abc_btn_check_material.xml": { - "Size": 464 + "res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png": { + "Size": 202 }, - "res/drawable/abc_btn_check_material_anim.xml": { - "Size": 816 + "res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png": { + "Size": 404 }, - "res/drawable/abc_btn_colored_material.xml": { - "Size": 344 + "res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png": { + "Size": 226 }, - "res/drawable/abc_btn_default_mtrl_shape.xml": { - "Size": 932 + "res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png": { + "Size": 215 }, - "res/drawable/abc_btn_radio_material.xml": { - "Size": 464 + "res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png": { + "Size": 389 }, - "res/drawable/abc_btn_radio_material_anim.xml": { - "Size": 816 + "res/drawable-hdpi-v4/abc_ic_star_black_16dp.png": { + "Size": 263 }, - "res/drawable/abc_cab_background_internal_bg.xml": { - "Size": 372 + "res/drawable-hdpi-v4/abc_ic_star_black_36dp.png": { + "Size": 522 }, - "res/drawable/abc_cab_background_top_material.xml": { - "Size": 336 + "res/drawable-hdpi-v4/abc_ic_star_black_48dp.png": { + "Size": 668 }, - "res/drawable/abc_dialog_material_background.xml": { - "Size": 716 + "res/drawable-hdpi-v4/abc_ic_star_half_black_16dp.png": { + "Size": 197 }, - "res/drawable/abc_edit_text_material.xml": { - "Size": 868 + "res/drawable-hdpi-v4/abc_ic_star_half_black_36dp.png": { + "Size": 328 }, - "res/drawable/abc_ic_ab_back_material.xml": { - "Size": 692 + "res/drawable-hdpi-v4/abc_ic_star_half_black_48dp.png": { + "Size": 431 }, - "res/drawable/abc_ic_arrow_drop_right_black_24dp.xml": { - "Size": 1000 + "res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png": { + "Size": 167 }, - "res/drawable/abc_ic_clear_material.xml": { - "Size": 684 + "res/drawable-hdpi-v4/abc_list_focused_holo.9.png": { + "Size": 244 }, - "res/drawable/abc_ic_go_search_api_material.xml": { - "Size": 640 + "res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png": { + "Size": 212 }, - "res/drawable/abc_ic_menu_overflow_material.xml": { - "Size": 792 + "res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png": { + "Size": 208 }, - "res/drawable/abc_ic_search_api_material.xml": { - "Size": 812 + "res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png": { + "Size": 208 }, - "res/drawable/abc_ic_voice_search_api_material.xml": { - "Size": 828 + "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png": { + "Size": 228 }, - "res/drawable/abc_item_background_holo_dark.xml": { - "Size": 1012 + "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png": { + "Size": 229 }, - "res/drawable/abc_item_background_holo_light.xml": { - "Size": 1012 + "res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png": { + "Size": 738 }, - "res/drawable/abc_list_divider_material.xml": { - "Size": 480 + "res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png": { + "Size": 1098 }, - "res/drawable/abc_list_selector_background_transition_holo_dark.xml": { - "Size": 424 + "res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png": { + "Size": 201 }, - "res/drawable/abc_list_selector_background_transition_holo_light.xml": { - "Size": 424 + "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png": { + "Size": 196 }, - "res/drawable/abc_list_selector_holo_dark.xml": { - "Size": 1064 + "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png": { + "Size": 272 }, - "res/drawable/abc_list_selector_holo_light.xml": { - "Size": 1064 + "res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png": { + "Size": 205 }, - "res/drawable/abc_ratingbar_indicator_material.xml": { - "Size": 664 + "res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png": { + "Size": 196 }, - "res/drawable/abc_ratingbar_material.xml": { - "Size": 664 + "res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png": { + "Size": 345 }, - "res/drawable/abc_ratingbar_small_material.xml": { - "Size": 664 + "res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png": { + "Size": 484 }, - "res/drawable/abc_seekbar_thumb_material.xml": { - "Size": 1100 + "res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png": { + "Size": 190 }, - "res/drawable/abc_seekbar_tick_mark_material.xml": { - "Size": 516 + "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_dark.png": { + "Size": 278 }, - "res/drawable/abc_seekbar_track_material.xml": { - "Size": 1408 + "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_light.png": { + "Size": 278 }, - "res/drawable/abc_spinner_textfield_background_material.xml": { - "Size": 1160 + "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_dark.png": { + "Size": 398 }, - "res/drawable/abc_switch_thumb_material.xml": { - "Size": 464 + "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_light.png": { + "Size": 396 }, - "res/drawable/abc_tab_indicator_material.xml": { - "Size": 468 + "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_dark.png": { + "Size": 263 }, - "res/drawable/abc_text_cursor_material.xml": { - "Size": 516 + "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_light.png": { + "Size": 262 }, - "res/drawable/abc_textfield_search_material.xml": { - "Size": 756 + "res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png": { + "Size": 186 }, - "res/drawable/abc_vector_test.xml": { - "Size": 612 - }, - "res/drawable/btn_checkbox_checked_mtrl.xml": { - "Size": 2688 - }, - "res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml": { - "Size": 688 - }, - "res/drawable/btn_checkbox_unchecked_mtrl.xml": { - "Size": 2660 - }, - "res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml": { - "Size": 688 - }, - "res/drawable/btn_radio_off_mtrl.xml": { - "Size": 1728 - }, - "res/drawable/btn_radio_off_to_on_mtrl_animation.xml": { - "Size": 680 - }, - "res/drawable/btn_radio_on_mtrl.xml": { - "Size": 1656 - }, - "res/drawable/btn_radio_on_to_off_mtrl_animation.xml": { - "Size": 680 - }, - "res/drawable/design_bottom_navigation_item_background.xml": { - "Size": 784 - }, - "res/drawable/design_fab_background.xml": { - "Size": 372 - }, - "res/drawable/design_password_eye.xml": { - "Size": 464 - }, - "res/drawable/design_snackbar_background.xml": { - "Size": 484 - }, - "res/drawable/ic_mtrl_chip_checked_black.xml": { - "Size": 600 - }, - "res/drawable/ic_mtrl_chip_checked_circle.xml": { - "Size": 940 - }, - "res/drawable/ic_mtrl_chip_close_circle.xml": { - "Size": 808 - }, - "res/drawable/mtrl_snackbar_background.xml": { - "Size": 484 - }, - "res/drawable/mtrl_tabs_default_indicator.xml": { - "Size": 628 - }, - "res/drawable/navigation_empty_icon.xml": { - "Size": 516 - }, - "res/drawable/notification_bg.xml": { - "Size": 532 - }, - "res/drawable/notification_bg_low.xml": { - "Size": 532 - }, - "res/drawable/notification_icon_background.xml": { - "Size": 372 - }, - "res/drawable/notification_tile_bg.xml": { - "Size": 304 - }, - "res/drawable/tooltip_frame_dark.xml": { - "Size": 484 - }, - "res/drawable/tooltip_frame_light.xml": { - "Size": 484 - }, - "res/drawable-anydpi-v21/design_ic_visibility.xml": { - "Size": 540 - }, - "res/drawable-anydpi-v21/design_ic_visibility_off.xml": { - "Size": 1144 - }, - "res/drawable-hdpi-v4/abc_ab_share_pack_mtrl_alpha.9.png": { - "Size": 272 - }, - "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_000.png": { - "Size": 227 - }, - "res/drawable-hdpi-v4/abc_btn_check_to_on_mtrl_015.png": { - "Size": 404 - }, - "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_000.png": { - "Size": 464 - }, - "res/drawable-hdpi-v4/abc_btn_radio_to_on_mtrl_015.png": { - "Size": 563 - }, - "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00001.9.png": { - "Size": 1096 - }, - "res/drawable-hdpi-v4/abc_btn_switch_to_on_mtrl_00012.9.png": { - "Size": 1243 - }, - "res/drawable-hdpi-v4/abc_cab_background_top_mtrl_alpha.9.png": { - "Size": 226 - }, - "res/drawable-hdpi-v4/abc_ic_commit_search_api_mtrl_alpha.png": { - "Size": 171 - }, - "res/drawable-hdpi-v4/abc_ic_menu_copy_mtrl_am_alpha.png": { - "Size": 202 - }, - "res/drawable-hdpi-v4/abc_ic_menu_cut_mtrl_alpha.png": { - "Size": 404 - }, - "res/drawable-hdpi-v4/abc_ic_menu_paste_mtrl_am_alpha.png": { - "Size": 226 - }, - "res/drawable-hdpi-v4/abc_ic_menu_selectall_mtrl_alpha.png": { - "Size": 215 - }, - "res/drawable-hdpi-v4/abc_ic_menu_share_mtrl_alpha.png": { - "Size": 389 - }, - "res/drawable-hdpi-v4/abc_ic_star_black_16dp.png": { - "Size": 263 - }, - "res/drawable-hdpi-v4/abc_ic_star_black_36dp.png": { - "Size": 522 - }, - "res/drawable-hdpi-v4/abc_ic_star_black_48dp.png": { - "Size": 668 - }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_16dp.png": { - "Size": 197 - }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_36dp.png": { - "Size": 328 - }, - "res/drawable-hdpi-v4/abc_ic_star_half_black_48dp.png": { - "Size": 431 - }, - "res/drawable-hdpi-v4/abc_list_divider_mtrl_alpha.9.png": { - "Size": 167 - }, - "res/drawable-hdpi-v4/abc_list_focused_holo.9.png": { - "Size": 244 - }, - "res/drawable-hdpi-v4/abc_list_longpressed_holo.9.png": { - "Size": 212 - }, - "res/drawable-hdpi-v4/abc_list_pressed_holo_dark.9.png": { - "Size": 208 - }, - "res/drawable-hdpi-v4/abc_list_pressed_holo_light.9.png": { - "Size": 208 - }, - "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_dark.9.png": { - "Size": 228 - }, - "res/drawable-hdpi-v4/abc_list_selector_disabled_holo_light.9.png": { - "Size": 229 - }, - "res/drawable-hdpi-v4/abc_menu_hardkey_panel_mtrl_mult.9.png": { - "Size": 738 - }, - "res/drawable-hdpi-v4/abc_popup_background_mtrl_mult.9.png": { - "Size": 1098 - }, - "res/drawable-hdpi-v4/abc_scrubber_control_off_mtrl_alpha.png": { - "Size": 201 - }, - "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_000.png": { - "Size": 196 - }, - "res/drawable-hdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png": { - "Size": 272 - }, - "res/drawable-hdpi-v4/abc_scrubber_primary_mtrl_alpha.9.png": { - "Size": 205 - }, - "res/drawable-hdpi-v4/abc_scrubber_track_mtrl_alpha.9.png": { - "Size": 196 - }, - "res/drawable-hdpi-v4/abc_spinner_mtrl_am_alpha.9.png": { - "Size": 345 - }, - "res/drawable-hdpi-v4/abc_switch_track_mtrl_alpha.9.png": { - "Size": 484 - }, - "res/drawable-hdpi-v4/abc_tab_indicator_mtrl_alpha.9.png": { - "Size": 190 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_dark.png": { - "Size": 278 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_left_mtrl_light.png": { - "Size": 278 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_dark.png": { - "Size": 398 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_middle_mtrl_light.png": { - "Size": 396 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_dark.png": { - "Size": 263 - }, - "res/drawable-hdpi-v4/abc_text_select_handle_right_mtrl_light.png": { - "Size": 262 - }, - "res/drawable-hdpi-v4/abc_textfield_activated_mtrl_alpha.9.png": { - "Size": 186 - }, - "res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png": { - "Size": 192 + "res/drawable-hdpi-v4/abc_textfield_default_mtrl_alpha.9.png": { + "Size": 192 }, "res/drawable-hdpi-v4/abc_textfield_search_activated_mtrl_alpha.9.png": { "Size": 178 @@ -874,12 +691,12 @@ "res/drawable-hdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 178 }, - "res/drawable-hdpi-v4/design_ic_visibility.png": { - "Size": 470 - }, "res/drawable-hdpi-v4/design_ic_visibility_off.png": { "Size": 507 }, + "res/drawable-hdpi-v4/design_ic_visibility.png": { + "Size": 470 + }, "res/drawable-hdpi-v4/icon.png": { "Size": 4762 }, @@ -889,12 +706,12 @@ "res/drawable-hdpi-v4/notification_bg_low_pressed.9.png": { "Size": 225 }, - "res/drawable-hdpi-v4/notification_bg_normal.9.png": { - "Size": 212 - }, "res/drawable-hdpi-v4/notification_bg_normal_pressed.9.png": { "Size": 225 }, + "res/drawable-hdpi-v4/notification_bg_normal.9.png": { + "Size": 212 + }, "res/drawable-hdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 107 }, @@ -1084,12 +901,12 @@ "res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 178 }, - "res/drawable-mdpi-v4/design_ic_visibility.png": { - "Size": 309 - }, "res/drawable-mdpi-v4/design_ic_visibility_off.png": { "Size": 351 }, + "res/drawable-mdpi-v4/design_ic_visibility.png": { + "Size": 309 + }, "res/drawable-mdpi-v4/icon.png": { "Size": 2200 }, @@ -1099,12 +916,12 @@ "res/drawable-mdpi-v4/notification_bg_low_pressed.9.png": { "Size": 223 }, - "res/drawable-mdpi-v4/notification_bg_normal.9.png": { - "Size": 215 - }, "res/drawable-mdpi-v4/notification_bg_normal_pressed.9.png": { "Size": 223 }, + "res/drawable-mdpi-v4/notification_bg_normal.9.png": { + "Size": 215 + }, "res/drawable-mdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 98 }, @@ -1312,12 +1129,12 @@ "res/drawable-xhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 182 }, - "res/drawable-xhdpi-v4/design_ic_visibility.png": { - "Size": 593 - }, "res/drawable-xhdpi-v4/design_ic_visibility_off.png": { "Size": 629 }, + "res/drawable-xhdpi-v4/design_ic_visibility.png": { + "Size": 593 + }, "res/drawable-xhdpi-v4/icon.png": { "Size": 7462 }, @@ -1327,12 +1144,12 @@ "res/drawable-xhdpi-v4/notification_bg_low_pressed.9.png": { "Size": 252 }, - "res/drawable-xhdpi-v4/notification_bg_normal.9.png": { - "Size": 221 - }, "res/drawable-xhdpi-v4/notification_bg_normal_pressed.9.png": { "Size": 247 }, + "res/drawable-xhdpi-v4/notification_bg_normal.9.png": { + "Size": 221 + }, "res/drawable-xhdpi-v4/notify_panel_notification_icon_bg.png": { "Size": 138 }, @@ -1477,12 +1294,12 @@ "res/drawable-xxhdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png": { "Size": 186 }, - "res/drawable-xxhdpi-v4/design_ic_visibility.png": { - "Size": 868 - }, "res/drawable-xxhdpi-v4/design_ic_visibility_off.png": { "Size": 884 }, + "res/drawable-xxhdpi-v4/design_ic_visibility.png": { + "Size": 868 + }, "res/drawable-xxhdpi-v4/icon.png": { "Size": 13092 }, @@ -1564,15 +1381,207 @@ "res/drawable-xxxhdpi-v4/abc_text_select_handle_right_mtrl_light.png": { "Size": 513 }, - "res/drawable-xxxhdpi-v4/design_ic_visibility.png": { - "Size": 1155 - }, "res/drawable-xxxhdpi-v4/design_ic_visibility_off.png": { "Size": 1201 }, + "res/drawable-xxxhdpi-v4/design_ic_visibility.png": { + "Size": 1155 + }, "res/drawable-xxxhdpi-v4/icon.png": { "Size": 20118 }, + "res/drawable/abc_btn_borderless_material.xml": { + "Size": 588 + }, + "res/drawable/abc_btn_check_material_anim.xml": { + "Size": 816 + }, + "res/drawable/abc_btn_check_material.xml": { + "Size": 464 + }, + "res/drawable/abc_btn_colored_material.xml": { + "Size": 344 + }, + "res/drawable/abc_btn_default_mtrl_shape.xml": { + "Size": 932 + }, + "res/drawable/abc_btn_radio_material_anim.xml": { + "Size": 816 + }, + "res/drawable/abc_btn_radio_material.xml": { + "Size": 464 + }, + "res/drawable/abc_cab_background_internal_bg.xml": { + "Size": 372 + }, + "res/drawable/abc_cab_background_top_material.xml": { + "Size": 336 + }, + "res/drawable/abc_dialog_material_background.xml": { + "Size": 716 + }, + "res/drawable/abc_edit_text_material.xml": { + "Size": 868 + }, + "res/drawable/abc_ic_ab_back_material.xml": { + "Size": 692 + }, + "res/drawable/abc_ic_arrow_drop_right_black_24dp.xml": { + "Size": 1000 + }, + "res/drawable/abc_ic_clear_material.xml": { + "Size": 684 + }, + "res/drawable/abc_ic_go_search_api_material.xml": { + "Size": 640 + }, + "res/drawable/abc_ic_menu_overflow_material.xml": { + "Size": 792 + }, + "res/drawable/abc_ic_search_api_material.xml": { + "Size": 812 + }, + "res/drawable/abc_ic_voice_search_api_material.xml": { + "Size": 828 + }, + "res/drawable/abc_item_background_holo_dark.xml": { + "Size": 1012 + }, + "res/drawable/abc_item_background_holo_light.xml": { + "Size": 1012 + }, + "res/drawable/abc_list_divider_material.xml": { + "Size": 480 + }, + "res/drawable/abc_list_selector_background_transition_holo_dark.xml": { + "Size": 424 + }, + "res/drawable/abc_list_selector_background_transition_holo_light.xml": { + "Size": 424 + }, + "res/drawable/abc_list_selector_holo_dark.xml": { + "Size": 1064 + }, + "res/drawable/abc_list_selector_holo_light.xml": { + "Size": 1064 + }, + "res/drawable/abc_ratingbar_indicator_material.xml": { + "Size": 664 + }, + "res/drawable/abc_ratingbar_material.xml": { + "Size": 664 + }, + "res/drawable/abc_ratingbar_small_material.xml": { + "Size": 664 + }, + "res/drawable/abc_seekbar_thumb_material.xml": { + "Size": 1100 + }, + "res/drawable/abc_seekbar_tick_mark_material.xml": { + "Size": 516 + }, + "res/drawable/abc_seekbar_track_material.xml": { + "Size": 1408 + }, + "res/drawable/abc_spinner_textfield_background_material.xml": { + "Size": 1160 + }, + "res/drawable/abc_switch_thumb_material.xml": { + "Size": 464 + }, + "res/drawable/abc_tab_indicator_material.xml": { + "Size": 468 + }, + "res/drawable/abc_text_cursor_material.xml": { + "Size": 516 + }, + "res/drawable/abc_textfield_search_material.xml": { + "Size": 756 + }, + "res/drawable/abc_vector_test.xml": { + "Size": 612 + }, + "res/drawable/btn_checkbox_checked_mtrl.xml": { + "Size": 2688 + }, + "res/drawable/btn_checkbox_checked_to_unchecked_mtrl_animation.xml": { + "Size": 688 + }, + "res/drawable/btn_checkbox_unchecked_mtrl.xml": { + "Size": 2660 + }, + "res/drawable/btn_checkbox_unchecked_to_checked_mtrl_animation.xml": { + "Size": 688 + }, + "res/drawable/btn_radio_off_mtrl.xml": { + "Size": 1728 + }, + "res/drawable/btn_radio_off_to_on_mtrl_animation.xml": { + "Size": 680 + }, + "res/drawable/btn_radio_on_mtrl.xml": { + "Size": 1656 + }, + "res/drawable/btn_radio_on_to_off_mtrl_animation.xml": { + "Size": 680 + }, + "res/drawable/design_bottom_navigation_item_background.xml": { + "Size": 784 + }, + "res/drawable/design_fab_background.xml": { + "Size": 372 + }, + "res/drawable/design_password_eye.xml": { + "Size": 464 + }, + "res/drawable/design_snackbar_background.xml": { + "Size": 484 + }, + "res/drawable/ic_mtrl_chip_checked_black.xml": { + "Size": 600 + }, + "res/drawable/ic_mtrl_chip_checked_circle.xml": { + "Size": 940 + }, + "res/drawable/ic_mtrl_chip_close_circle.xml": { + "Size": 808 + }, + "res/drawable/mtrl_snackbar_background.xml": { + "Size": 484 + }, + "res/drawable/mtrl_tabs_default_indicator.xml": { + "Size": 628 + }, + "res/drawable/navigation_empty_icon.xml": { + "Size": 516 + }, + "res/drawable/notification_bg_low.xml": { + "Size": 532 + }, + "res/drawable/notification_bg.xml": { + "Size": 532 + }, + "res/drawable/notification_icon_background.xml": { + "Size": 372 + }, + "res/drawable/notification_tile_bg.xml": { + "Size": 304 + }, + "res/drawable/tooltip_frame_dark.xml": { + "Size": 484 + }, + "res/drawable/tooltip_frame_light.xml": { + "Size": 484 + }, + "res/interpolator-v21/mtrl_fast_out_linear_in.xml": { + "Size": 400 + }, + "res/interpolator-v21/mtrl_fast_out_slow_in.xml": { + "Size": 400 + }, + "res/interpolator-v21/mtrl_linear_out_slow_in.xml": { + "Size": 400 + }, "res/interpolator/btn_checkbox_checked_mtrl_animation_interpolator_0.xml": { "Size": 316 }, @@ -1600,20 +1609,53 @@ "res/interpolator/mtrl_fast_out_slow_in.xml": { "Size": 144 }, + "res/interpolator/mtrl_linear_out_slow_in.xml": { + "Size": 136 + }, "res/interpolator/mtrl_linear.xml": { "Size": 132 }, - "res/interpolator/mtrl_linear_out_slow_in.xml": { - "Size": 136 + "res/layout-sw600dp-v13/design_layout_snackbar.xml": { + "Size": 528 }, - "res/interpolator-v21/mtrl_fast_out_linear_in.xml": { - "Size": 400 + "res/layout-sw600dp-v13/mtrl_layout_snackbar.xml": { + "Size": 528 }, - "res/interpolator-v21/mtrl_fast_out_slow_in.xml": { - "Size": 400 + "res/layout-v16/notification_template_custom_big.xml": { + "Size": 3208 }, - "res/interpolator-v21/mtrl_linear_out_slow_in.xml": { - "Size": 400 + "res/layout-v21/abc_screen_toolbar.xml": { + "Size": 1504 + }, + "res/layout-v21/fallbacktoolbardonotuse.xml": { + "Size": 496 + }, + "res/layout-v21/notification_action_tombstone.xml": { + "Size": 1228 + }, + "res/layout-v21/notification_action.xml": { + "Size": 1052 + }, + "res/layout-v21/notification_template_custom_big.xml": { + "Size": 2456 + }, + "res/layout-v21/notification_template_icon_group.xml": { + "Size": 988 + }, + "res/layout-v21/toolbar.xml": { + "Size": 496 + }, + "res/layout-v22/abc_alert_dialog_button_bar_material.xml": { + "Size": 1584 + }, + "res/layout-v26/abc_screen_toolbar.xml": { + "Size": 1560 + }, + "res/layout-watch-v20/abc_alert_dialog_button_bar_material.xml": { + "Size": 1208 + }, + "res/layout-watch-v20/abc_alert_dialog_title_material.xml": { + "Size": 1352 }, "res/layout/abc_action_bar_title_item.xml": { "Size": 872 @@ -1633,12 +1675,12 @@ "res/layout/abc_action_mode_close_item_material.xml": { "Size": 840 }, - "res/layout/abc_activity_chooser_view.xml": { - "Size": 1684 - }, "res/layout/abc_activity_chooser_view_list_item.xml": { "Size": 1304 }, + "res/layout/abc_activity_chooser_view.xml": { + "Size": 1684 + }, "res/layout/abc_alert_dialog_button_bar_material.xml": { "Size": 1536 }, @@ -1678,12 +1720,12 @@ "res/layout/abc_screen_content_include.xml": { "Size": 548 }, - "res/layout/abc_screen_simple.xml": { - "Size": 832 - }, "res/layout/abc_screen_simple_overlay_action_mode.xml": { "Size": 792 }, + "res/layout/abc_screen_simple.xml": { + "Size": 832 + }, "res/layout/abc_screen_toolbar.xml": { "Size": 1452 }, @@ -1717,12 +1759,12 @@ "res/layout/design_bottom_sheet_dialog.xml": { "Size": 1184 }, - "res/layout/design_layout_snackbar.xml": { - "Size": 528 - }, "res/layout/design_layout_snackbar_include.xml": { "Size": 1444 }, + "res/layout/design_layout_snackbar.xml": { + "Size": 528 + }, "res/layout/design_layout_tab_icon.xml": { "Size": 408 }, @@ -1732,9 +1774,6 @@ "res/layout/design_menu_item_action_area.xml": { "Size": 320 }, - "res/layout/design_navigation_item.xml": { - "Size": 536 - }, "res/layout/design_navigation_item_header.xml": { "Size": 440 }, @@ -1744,12 +1783,15 @@ "res/layout/design_navigation_item_subheader.xml": { "Size": 564 }, - "res/layout/design_navigation_menu.xml": { - "Size": 528 + "res/layout/design_navigation_item.xml": { + "Size": 536 }, "res/layout/design_navigation_menu_item.xml": { "Size": 856 }, + "res/layout/design_navigation_menu.xml": { + "Size": 528 + }, "res/layout/design_text_input_password_icon.xml": { "Size": 564 }, @@ -1765,35 +1807,35 @@ "res/layout/main.xml": { "Size": 544 }, - "res/layout/mtrl_layout_snackbar.xml": { - "Size": 528 - }, "res/layout/mtrl_layout_snackbar_include.xml": { "Size": 1404 }, - "res/layout/notification_action.xml": { - "Size": 1156 + "res/layout/mtrl_layout_snackbar.xml": { + "Size": 528 }, "res/layout/notification_action_tombstone.xml": { "Size": 1332 }, + "res/layout/notification_action.xml": { + "Size": 1156 + }, "res/layout/notification_media_action.xml": { "Size": 564 }, "res/layout/notification_media_cancel_action.xml": { "Size": 744 }, - "res/layout/notification_template_big_media.xml": { - "Size": 1696 - }, "res/layout/notification_template_big_media_custom.xml": { "Size": 3044 }, + "res/layout/notification_template_big_media_narrow_custom.xml": { + "Size": 3216 + }, "res/layout/notification_template_big_media_narrow.xml": { "Size": 1824 }, - "res/layout/notification_template_big_media_narrow_custom.xml": { - "Size": 3216 + "res/layout/notification_template_big_media.xml": { + "Size": 1696 }, "res/layout/notification_template_icon_group.xml": { "Size": 392 @@ -1801,12 +1843,12 @@ "res/layout/notification_template_lines_media.xml": { "Size": 2872 }, - "res/layout/notification_template_media.xml": { - "Size": 1292 - }, "res/layout/notification_template_media_custom.xml": { "Size": 2756 }, + "res/layout/notification_template_media.xml": { + "Size": 1292 + }, "res/layout/notification_template_part_chronometer.xml": { "Size": 440 }, @@ -1837,51 +1879,9 @@ "res/layout/toolbar.xml": { "Size": 452 }, - "res/layout-sw600dp-v13/design_layout_snackbar.xml": { - "Size": 528 - }, - "res/layout-sw600dp-v13/mtrl_layout_snackbar.xml": { - "Size": 528 - }, - "res/layout-v16/notification_template_custom_big.xml": { - "Size": 3208 - }, - "res/layout-v21/abc_screen_toolbar.xml": { - "Size": 1504 - }, - "res/layout-v21/fallbacktoolbardonotuse.xml": { - "Size": 496 - }, - "res/layout-v21/notification_action.xml": { - "Size": 1052 - }, - "res/layout-v21/notification_action_tombstone.xml": { - "Size": 1228 - }, - "res/layout-v21/notification_template_custom_big.xml": { - "Size": 2456 - }, - "res/layout-v21/notification_template_icon_group.xml": { - "Size": 988 - }, - "res/layout-v21/toolbar.xml": { - "Size": 496 - }, - "res/layout-v22/abc_alert_dialog_button_bar_material.xml": { - "Size": 1584 - }, - "res/layout-v26/abc_screen_toolbar.xml": { - "Size": 1560 - }, - "res/layout-watch-v20/abc_alert_dialog_button_bar_material.xml": { - "Size": 1208 - }, - "res/layout-watch-v20/abc_alert_dialog_title_material.xml": { - "Size": 1352 - }, "resources.arsc": { "Size": 341040 } }, - "PackageSize": 9566366 + "PackageSize": 9570462 } \ No newline at end of file From 5af6d316ed02fad579755c86a1d7ebdd693cf49c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jun 2022 16:32:14 +0200 Subject: [PATCH 07/33] Require explicit opt-in to enable the feature --- .../ILLink/ILLink.Substitutions.xml | 4 ++ .../AndroidMessageHandler.cs | 31 +++-------- .../NTAuthenticationHandler.cs | 53 ++++++++----------- ...soft.Android.Sdk.DefaultProperties.targets | 1 + .../Microsoft.Android.Sdk.ILLink.targets | 4 ++ .../Tasks/LinkerTests.cs | 22 ++++++++ 6 files changed, 60 insertions(+), 55 deletions(-) diff --git a/src/Mono.Android/ILLink/ILLink.Substitutions.xml b/src/Mono.Android/ILLink/ILLink.Substitutions.xml index 96dff01b2b6..93de109d6d2 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/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index f917db2bb16..6779acf392d 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -139,33 +139,9 @@ public CookieContainer CookieContainer public bool UseProxy { get; set; } = true; -#if !MONOANDROID1_0 - IWebProxy? _proxy; - ICredentials? _credentials; - NTAuthenticationHandler.Helper? _ntAuthHelper; - - public IWebProxy? Proxy - { - get => _proxy; - set { - _proxy = value; - _ntAuthHelper ??= new NTAuthenticationHandler.Helper (this); - } - } - - public ICredentials? Credentials - { - get => _credentials; - set { - _credentials = value; - _ntAuthHelper ??= new NTAuthenticationHandler.Helper (this); - } - } -#else public IWebProxy? Proxy { get; set; } public ICredentials? Credentials { get; set; } -#endif public bool AllowAutoRedirect { get; set; } = true; @@ -288,6 +264,11 @@ public int MaxAutomaticRedirections /// public TimeSpan ReadTimeout { get; set; } = TimeSpan.FromHours (24); +#if !MONOANDROID1_0 + static bool NTAuthenticationIsEnabled => + AppContext.TryGetSwitch ("Xamarin.Android.Net.UseNTAuthentication", out bool isEnabled) && isEnabled; +#endif + /// /// /// Specifies the connect timeout @@ -360,7 +341,7 @@ string EncodeUrl (Uri url) var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); #if !MONOANDROID1_0 - if (RequestNeedsAuthorization && _ntAuthHelper != null && _ntAuthHelper.RequestNeedsNTAuthentication (request, out var ntAuth)) { + if (NTAuthenticationIsEnabled && RequestNeedsAuthorization && NTAuthenticationHandler.RequestNeedsNTAuthentication (this, request, out var ntAuth)) { var authenticatedResponse = await ntAuth.ResendRequestWithAuthAsync (cancellationToken).ConfigureAwait (false); if (authenticatedResponse != null) return authenticatedResponse; diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs index ec9c6de9b44..db22d16374d 100644 --- a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs @@ -17,42 +17,35 @@ internal sealed class NTAuthenticationHandler { const int MaxRequests = 10; - internal sealed class Helper + internal static bool RequestNeedsNTAuthentication ( + AndroidMessageHandler handler, + HttpRequestMessage request, + [NotNullWhen (true)] out NTAuthenticationHandler? ntAuthHandler) { - readonly AndroidMessageHandler _handler; - - internal Helper (AndroidMessageHandler handler) - { - _handler = handler; - } - - internal bool RequestNeedsNTAuthentication (HttpRequestMessage request, [NotNullWhen (true)] out NTAuthenticationHandler? ntAuthHandler) - { - IEnumerable requestedAuthentication = _handler.RequestedAuthentication ?? Enumerable.Empty (); - foreach (var auth in requestedAuthentication) { - if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { - var credentials = auth.UseProxyAuthentication ? _handler.Proxy?.Credentials : _handler.Credentials; - var correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); - - if (correspondingCredentials != null) { - ntAuthHandler = new NTAuthenticationHandler (_handler, request, authType, auth.UseProxyAuthentication, correspondingCredentials); - return true; - } + IEnumerable requestedAuthentication = handler.RequestedAuthentication ?? Enumerable.Empty (); + foreach (var auth in requestedAuthentication) { + if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { + var credentials = auth.UseProxyAuthentication ? handler.Proxy?.Credentials : handler.Credentials; + var correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); + + if (correspondingCredentials != null) { + ntAuthHandler = new NTAuthenticationHandler (handler, request, authType, auth.UseProxyAuthentication, correspondingCredentials); + return true; } } - - ntAuthHandler = null; - return false; } - static bool TryGetSupportedAuthType (string challenge, out string authType) - { - var spaceIndex = challenge.IndexOf (' '); - authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); + ntAuthHandler = null; + return false; + } - return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || - authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); - } + static bool TryGetSupportedAuthType (string challenge, out string authType) + { + var spaceIndex = challenge.IndexOf (' '); + authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); + + return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || + authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); } readonly AndroidMessageHandler _handler; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index baa52a204bc..ea392895cd9 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -104,6 +104,7 @@ false + false True diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets index 67d1bbba119..db33dba4fb1 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets @@ -28,6 +28,10 @@ This file contains the .NET 5-specific targets to customize ILLink Condition="'$(VSAndroidDesigner)' != ''" Value="$(VSAndroidDesigner)" Trim="true" /> + Date: Fri, 10 Jun 2022 12:24:20 +0200 Subject: [PATCH 08/33] Fix switch default value --- .../AndroidMessageHandlerNTAuthenticationTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs index d68ff12c0a7..dc1e84b86d0 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs @@ -10,6 +10,12 @@ namespace Xamarin.Android.NetTests { [TestFixture] public sealed class AndroidMessageHandlerNTAuthenticationTests { + [SetUp] + public void SetUp () + { + AppContext.SetSwitch ("Xamarin.Android.Net.UseNTAuthentication", true); + } + [Test] public async Task RequestWithoutCredentialsFails () { From 26d38dfdac59517ef1ecf64af25521e0fc7134d9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 10 Jun 2022 15:51:52 +0200 Subject: [PATCH 09/33] Update linker tests --- .../Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index ff56617425c..d455496a2fa 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -470,22 +470,23 @@ public void TypeRegistrationsFallback ([Values (true, false)] bool enabled) } [Test] - public void UseNTAuthentication ([Values (true, false)] bool enabled) + public void UseNTAuthentication ([Values (true, false, null)] bool? enabled) { if (!Builder.UseDotNet) Assert.Ignore ("Test only valid on .NET 6"); var proj = new XamarinAndroidApplicationProject () { IsRelease = true }; - proj.SetProperty (proj.ActiveConfigurationProperties, "UseNTAuthentication", enabled.ToString ()); + if (enabled.HasValue) + proj.SetProperty ("UseNTAuthentication", enabled.ToString ()); using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - var assemblyFile = "Mono.Android.dll"; - var assemblyPath = BuildTest.GetLinkedPath (b, true, assemblyFile); + var assemblyPath = BuildTest.GetLinkedPath (b, true, "Mono.Android.dll"); using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) { Assert.IsTrue (assembly != null); var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NTAuthenticationHandler"); + enabled ??= false; Assert.IsTrue ((td != null) == enabled); } } From 259a7c6f15fcfd03b363a4c8386e24565f120fb9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 14 Jun 2022 14:43:37 +0200 Subject: [PATCH 10/33] Renaming --- .../ILLink/ILLink.Substitutions.xml | 4 +- src/Mono.Android/Mono.Android.csproj | 2 +- .../AndroidMessageHandler.cs | 8 ++-- ...er.cs => NegotiateAuthenticationHelper.cs} | 43 +++++++++---------- ...soft.Android.Sdk.DefaultProperties.targets | 2 +- .../Microsoft.Android.Sdk.ILLink.targets | 6 +-- .../Tasks/LinkerTests.cs | 4 +- .../Mono.Android.NET-Tests.csproj | 2 +- ...ageHandlerNegotiateAuthenticationTests.cs} | 4 +- 9 files changed, 37 insertions(+), 38 deletions(-) rename src/Mono.Android/Xamarin.Android.Net/{NTAuthenticationHandler.cs => NegotiateAuthenticationHelper.cs} (84%) rename tests/Mono.Android-Tests/Xamarin.Android.Net/{AndroidMessageHandlerNTAuthenticationTests.cs => AndroidMessageHandlerNegotiateAuthenticationTests.cs} (96%) diff --git a/src/Mono.Android/ILLink/ILLink.Substitutions.xml b/src/Mono.Android/ILLink/ILLink.Substitutions.xml index 93de109d6d2..55ac10132dc 100644 --- a/src/Mono.Android/ILLink/ILLink.Substitutions.xml +++ b/src/Mono.Android/ILLink/ILLink.Substitutions.xml @@ -9,8 +9,8 @@ - - + + diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 00763fdb500..4c24e309709 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -368,7 +368,7 @@ - + diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index 6779acf392d..4e6ccfc58be 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -265,8 +265,8 @@ public int MaxAutomaticRedirections public TimeSpan ReadTimeout { get; set; } = TimeSpan.FromHours (24); #if !MONOANDROID1_0 - static bool NTAuthenticationIsEnabled => - AppContext.TryGetSwitch ("Xamarin.Android.Net.UseNTAuthentication", out bool isEnabled) && isEnabled; + static bool NegotiateAuthenticationIsEnabled => + AppContext.TryGetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", out bool isEnabled) && isEnabled; #endif /// @@ -341,8 +341,8 @@ string EncodeUrl (Uri url) var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); #if !MONOANDROID1_0 - if (NTAuthenticationIsEnabled && RequestNeedsAuthorization && NTAuthenticationHandler.RequestNeedsNTAuthentication (this, request, out var ntAuth)) { - var authenticatedResponse = await ntAuth.ResendRequestWithAuthAsync (cancellationToken).ConfigureAwait (false); + if (NegotiateAuthenticationIsEnabled && RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var negotiateAuthentication)) { + var authenticatedResponse = await negotiateAuthentication.SendWithAuthAsync (request, cancellationToken).ConfigureAwait (false); if (authenticatedResponse != null) return authenticatedResponse; } diff --git a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs similarity index 84% rename from src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs rename to src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs index db22d16374d..8c001bf0152 100644 --- a/src/Mono.Android/Xamarin.Android.Net/NTAuthenticationHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs @@ -13,14 +13,14 @@ namespace Xamarin.Android.Net { // This code is heavily inspired by System.Net.Http.AuthenticationHelper - internal sealed class NTAuthenticationHandler + internal sealed class NegotiateAuthenticationHelper { const int MaxRequests = 10; - internal static bool RequestNeedsNTAuthentication ( + internal static bool RequestNeedsNegotiateAuthentication ( AndroidMessageHandler handler, HttpRequestMessage request, - [NotNullWhen (true)] out NTAuthenticationHandler? ntAuthHandler) + [NotNullWhen (true)] out NegotiateAuthenticationHelper? negotiateAuthentication) { IEnumerable requestedAuthentication = handler.RequestedAuthentication ?? Enumerable.Empty (); foreach (var auth in requestedAuthentication) { @@ -29,13 +29,13 @@ internal sealed class NTAuthenticationHandler var correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); if (correspondingCredentials != null) { - ntAuthHandler = new NTAuthenticationHandler (handler, request, authType, auth.UseProxyAuthentication, correspondingCredentials); + negotiateAuthentication = new NegotiateAuthenticationHelper (handler, authType, auth.UseProxyAuthentication, correspondingCredentials); return true; } } } - ntAuthHandler = null; + negotiateAuthentication = null; return false; } @@ -49,32 +49,30 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) } readonly AndroidMessageHandler _handler; - readonly HttpRequestMessage _request; readonly string _authType; readonly bool _isProxyAuth; readonly NetworkCredential _credentials; - private NTAuthenticationHandler ( + private NegotiateAuthenticationHelper ( AndroidMessageHandler handler, - HttpRequestMessage request, string authType, bool isProxyAuth, NetworkCredential credentials) { _handler = handler; - _request = request; _authType = authType; _isProxyAuth = isProxyAuth; _credentials = credentials; } - internal async Task ResendRequestWithAuthAsync (CancellationToken cancellationToken) + internal async Task SendWithAuthAsync (HttpRequestMessage request, CancellationToken cancellationToken) { + // TODO: replace with NegotiateAuthentication once it's available in dotnet/runtime var authContext = new NTAuthentication ( isServer: false, _authType, _credentials, - spn: await GetSpn (cancellationToken).ConfigureAwait (false), + spn: await GetSpn (request, cancellationToken).ConfigureAwait (false), requestedContextFlags: GetRequestedContextFlags (), channelBinding: null); @@ -84,14 +82,14 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) _handler.PreAuthenticate = false; try { - return await DoResendRequestWithAuthAsync (authContext, cancellationToken); + return await DoSendWithAuthAsync (request, authContext, cancellationToken); } finally { _handler.PreAuthenticate = originalPreAuthenticate; authContext.CloseContext (); } } - async Task DoResendRequestWithAuthAsync (NTAuthentication authContext, CancellationToken cancellationToken) + async Task DoSendWithAuthAsync (HttpRequestMessage request, NTAuthentication authContext, CancellationToken cancellationToken) { HttpResponseMessage? response = null; @@ -108,12 +106,12 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) var headerValue = new AuthenticationHeaderValue (_authType, challengeResponse); if (_isProxyAuth) { - _request.Headers.ProxyAuthorization = headerValue; + request.Headers.ProxyAuthorization = headerValue; } else { - _request.Headers.Authorization = headerValue; + request.Headers.Authorization = headerValue; } - response = await _handler.DoSendAsync (_request, cancellationToken).ConfigureAwait (false); + response = await _handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); // we need to drain the content otherwise the next request // won't reuse the same TCP socket and persistent auth won't work @@ -134,24 +132,24 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) return response; } - async Task GetSpn (CancellationToken cancellationToken) + async Task GetSpn (HttpRequestMessage request, CancellationToken cancellationToken) { - var hostName = await GetHostName (cancellationToken); + var hostName = await GetHostName (request, cancellationToken); return $"HTTP/{hostName}"; } - async Task GetHostName (CancellationToken cancellationToken) + async Task GetHostName (HttpRequestMessage request, CancellationToken cancellationToken) { // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. - if (!_isProxyAuth && _request.Headers.Host != null) { + if (!_isProxyAuth && request.Headers.Host != null) { // Use the host name without any normalization. - return _request.Headers.Host; + return request.Headers.Host; } - var requestUri = _request.RequestUri!; + var requestUri = request.RequestUri!; var authUri = _isProxyAuth ? _handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; // Need to use FQDN normalized host so that CNAME's are traversed. @@ -194,6 +192,7 @@ bool IsAuthenticationChallenge (HttpResponseMessage response) ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired : response.StatusCode == HttpStatusCode.Unauthorized; + // This class will be removed once the new NegotiateAuthentication class is available in dotnet/runtime private sealed class NTAuthentication { const string AssemblyName = "System.Net.Http"; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index ea392895cd9..f0a621a1f45 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -104,7 +104,7 @@ false - false + false True diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets index db33dba4fb1..d4d8bf6b553 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets @@ -28,9 +28,9 @@ This file contains the .NET 5-specific targets to customize ILLink Condition="'$(VSAndroidDesigner)' != ''" Value="$(VSAndroidDesigner)" Trim="true" /> - - + diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs similarity index 96% rename from tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs rename to tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index dc1e84b86d0..e7d39109585 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNTAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -8,12 +8,12 @@ namespace Xamarin.Android.NetTests { [TestFixture] - public sealed class AndroidMessageHandlerNTAuthenticationTests + public sealed class AndroidMessageHandlerNegotiateAuthenticationTests { [SetUp] public void SetUp () { - AppContext.SetSwitch ("Xamarin.Android.Net.UseNTAuthentication", true); + AppContext.SetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", true); } [Test] From ce06b06025965c4660d08fae6d71faa7550d9184 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 15 Jun 2022 15:13:55 +0200 Subject: [PATCH 11/33] Make helper fully static + update linker tests --- .../AndroidMessageHandler.cs | 10 +- .../NegotiateAuthenticationHelper.cs | 126 ++++++++++-------- .../Tasks/LinkerTests.cs | 24 +++- 3 files changed, 91 insertions(+), 69 deletions(-) diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index 4e6ccfc58be..06a5ac9eeb0 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -341,10 +341,12 @@ string EncodeUrl (Uri url) var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); #if !MONOANDROID1_0 - if (NegotiateAuthenticationIsEnabled && RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var negotiateAuthentication)) { - var authenticatedResponse = await negotiateAuthentication.SendWithAuthAsync (request, cancellationToken).ConfigureAwait (false); - if (authenticatedResponse != null) - return authenticatedResponse; + if (NegotiateAuthenticationIsEnabled) { + if (RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var authData, out var credentials)) { + var authenticatedResponse = await NegotiateAuthenticationHelper.SendWithAuthAsync (this, request, authData, credentials, cancellationToken).ConfigureAwait (false); + if (authenticatedResponse != null) + return authenticatedResponse; + } } #endif diff --git a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs index 8c001bf0152..40b8da4181d 100644 --- a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs +++ b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs @@ -13,83 +13,85 @@ namespace Xamarin.Android.Net { // This code is heavily inspired by System.Net.Http.AuthenticationHelper - internal sealed class NegotiateAuthenticationHelper + internal static class NegotiateAuthenticationHelper { const int MaxRequests = 10; internal static bool RequestNeedsNegotiateAuthentication ( AndroidMessageHandler handler, HttpRequestMessage request, - [NotNullWhen (true)] out NegotiateAuthenticationHelper? negotiateAuthentication) + [NotNullWhen (true)] out AuthenticationData? requestedAuthentication, + [NotNullWhen (true)] out NetworkCredential? correspondingCredentials) { - IEnumerable requestedAuthentication = handler.RequestedAuthentication ?? Enumerable.Empty (); - foreach (var auth in requestedAuthentication) { + IEnumerable authenticationData = handler.RequestedAuthentication ?? Enumerable.Empty (); + foreach (var auth in authenticationData) { if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { var credentials = auth.UseProxyAuthentication ? handler.Proxy?.Credentials : handler.Credentials; - var correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); + correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); if (correspondingCredentials != null) { - negotiateAuthentication = new NegotiateAuthenticationHelper (handler, authType, auth.UseProxyAuthentication, correspondingCredentials); + requestedAuthentication = auth; return true; } } } - negotiateAuthentication = null; + requestedAuthentication = null; + correspondingCredentials = null; return false; } - static bool TryGetSupportedAuthType (string challenge, out string authType) - { - var spaceIndex = challenge.IndexOf (' '); - authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); - - return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || - authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); - } - - readonly AndroidMessageHandler _handler; - readonly string _authType; - readonly bool _isProxyAuth; - readonly NetworkCredential _credentials; - - private NegotiateAuthenticationHelper ( + internal static async Task SendWithAuthAsync ( AndroidMessageHandler handler, - string authType, - bool isProxyAuth, - NetworkCredential credentials) + HttpRequestMessage request, + AuthenticationData requestedAuthentication, + NetworkCredential credentials, + CancellationToken cancellationToken) { - _handler = handler; - _authType = authType; - _isProxyAuth = isProxyAuth; - _credentials = credentials; - } + if (!TryGetSupportedAuthType (requestedAuthentication.Challenge, out var authType)) { + return null; + } - internal async Task SendWithAuthAsync (HttpRequestMessage request, CancellationToken cancellationToken) - { + var isProxyAuth = requestedAuthentication.UseProxyAuthentication; + var spn = await GetSpn (handler, request, isProxyAuth, cancellationToken).ConfigureAwait (false); // TODO: replace with NegotiateAuthentication once it's available in dotnet/runtime var authContext = new NTAuthentication ( isServer: false, - _authType, - _credentials, - spn: await GetSpn (request, cancellationToken).ConfigureAwait (false), - requestedContextFlags: GetRequestedContextFlags (), + authType, + credentials, + spn: spn, + requestedContextFlags: GetRequestedContextFlags (isProxyAuth), channelBinding: null); // we need to make sure that the handler doesn't override the authorization header // with the user defined pre-authentication data - var originalPreAuthenticate = _handler.PreAuthenticate; - _handler.PreAuthenticate = false; + var originalPreAuthenticate = handler.PreAuthenticate; + handler.PreAuthenticate = false; try { - return await DoSendWithAuthAsync (request, authContext, cancellationToken); + return await DoSendWithAuthAsync (handler, request, authContext, authType, isProxyAuth, cancellationToken); } finally { - _handler.PreAuthenticate = originalPreAuthenticate; + handler.PreAuthenticate = originalPreAuthenticate; authContext.CloseContext (); } } - async Task DoSendWithAuthAsync (HttpRequestMessage request, NTAuthentication authContext, CancellationToken cancellationToken) + static bool TryGetSupportedAuthType (string challenge, out string authType) + { + var spaceIndex = challenge.IndexOf (' '); + authType = spaceIndex == -1 ? challenge : challenge.Substring (0, spaceIndex); + + return authType.Equals ("NTLM", StringComparison.OrdinalIgnoreCase) || + authType.Equals ("Negotiate", StringComparison.OrdinalIgnoreCase); + } + + static async Task DoSendWithAuthAsync ( + AndroidMessageHandler handler, + HttpRequestMessage request, + NTAuthentication authContext, + string authType, + bool isProxyAuth, + CancellationToken cancellationToken) { HttpResponseMessage? response = null; @@ -104,24 +106,24 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) break; } - var headerValue = new AuthenticationHeaderValue (_authType, challengeResponse); - if (_isProxyAuth) { + var headerValue = new AuthenticationHeaderValue (authType, challengeResponse); + if (isProxyAuth) { request.Headers.ProxyAuthorization = headerValue; } else { request.Headers.Authorization = headerValue; } - response = await _handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); + response = await handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); // we need to drain the content otherwise the next request // won't reuse the same TCP socket and persistent auth won't work await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); - if (authContext.IsCompleted || !TryGetChallenge (response, out challenge)) { + if (authContext.IsCompleted || !TryGetChallenge (response, authType, isProxyAuth, out challenge)) { break; } - if (!IsAuthenticationChallenge (response)) { + if (!IsAuthenticationChallenge (response, isProxyAuth)) { // Tail response for Negotiate on successful authentication. // Validate it before we proceed. authContext.GetOutgoingBlob (challenge); @@ -132,25 +134,33 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) return response; } - async Task GetSpn (HttpRequestMessage request, CancellationToken cancellationToken) + static async Task GetSpn ( + AndroidMessageHandler handler, + HttpRequestMessage request, + bool isProxyAuth, + CancellationToken cancellationToken) { - var hostName = await GetHostName (request, cancellationToken); + var hostName = await GetHostName (handler, request, isProxyAuth, cancellationToken); return $"HTTP/{hostName}"; } - async Task GetHostName (HttpRequestMessage request, CancellationToken cancellationToken) + static async Task GetHostName ( + AndroidMessageHandler handler, + HttpRequestMessage request, + bool isProxyAuth, + CancellationToken cancellationToken) { // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. - if (!_isProxyAuth && request.Headers.Host != null) { + if (!isProxyAuth && request.Headers.Host != null) { // Use the host name without any normalization. return request.Headers.Host; } var requestUri = request.RequestUri!; - var authUri = _isProxyAuth ? _handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; + var authUri = isProxyAuth ? handler.Proxy?.GetProxy (requestUri) ?? requestUri : requestUri; // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. @@ -164,7 +174,7 @@ async Task GetHostName (HttpRequestMessage request, CancellationToken ca } } - int GetRequestedContextFlags () + static int GetRequestedContextFlags (bool isProxyAuth) { // the ContextFlagsPal is internal type in dotnet/runtime and we can't // use it directly here so we have to use ints directly @@ -173,22 +183,22 @@ int GetRequestedContextFlags () // When connecting to proxy server don't enforce the integrity to avoid // compatibility issues. The assumption is that the proxy server comes // from a trusted source. - if (!_isProxyAuth) { + if (!isProxyAuth) { contextFlags |= 0x00010000; // ContextFlagsPal.InitIntegrity } return contextFlags; } - bool TryGetChallenge (HttpResponseMessage response, [NotNullWhen (true)] out string? challenge) + static bool TryGetChallenge (HttpResponseMessage response, string authType, bool isProxyAuth, [NotNullWhen (true)] out string? challenge) { - var responseHeaderValues = _isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; - challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == _authType)?.Parameter; + var responseHeaderValues = isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; + challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == authType)?.Parameter; return !string.IsNullOrEmpty (challenge); } - bool IsAuthenticationChallenge (HttpResponseMessage response) - => _isProxyAuth + static bool IsAuthenticationChallenge (HttpResponseMessage response, bool isProxyAuth) + => isProxyAuth ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired : response.StatusCode == HttpStatusCode.Unauthorized; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index c4d84b68b17..4b064f92ee6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -470,14 +470,21 @@ public void TypeRegistrationsFallback ([Values (true, false)] bool enabled) } [Test] - public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool? enabled) + public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool? useNegotiateAuthentication) { if (!Builder.UseDotNet) Assert.Ignore ("Test only valid on .NET 6"); - var proj = new XamarinAndroidApplicationProject () { IsRelease = true }; - if (enabled.HasValue) - proj.SetProperty ("AndroidUseNegotiateAuthentication", enabled.ToString ()); + var proj = new XamarinAndroidApplicationProject { IsRelease = true }; + proj.AddReferences ("System.Net.Http"); + proj.MainActivity = proj.DefaultMainActivity.Replace ( + "base.OnCreate (bundle);", + "base.OnCreate (bundle);\n" + + "var client = new System.Net.Http.HttpClient (new Xamarin.Android.Net.AndroidMessageHandler ());\n" + + "client.GetAsync (\"https://microsoft.com\").GetAwaiter ().GetResult ();"); + + if (useNegotiateAuthentication.HasValue) + proj.SetProperty ("AndroidUseNegotiateAuthentication", useNegotiateAuthentication.ToString ()); using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); @@ -485,9 +492,12 @@ public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) { Assert.IsTrue (assembly != null); - var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NTAuthenticationHandler"); - enabled ??= false; - Assert.IsTrue ((td != null) == enabled); + var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NegotiateAuthenticationHelper"); + if (useNegotiateAuthentication.HasValue && useNegotiateAuthentication.Value) { + Assert.IsNotNull (td, "NegotiateAuthenticationHelper shouldn't have been linked out"); + } else { + Assert.IsNull (td, "NegotiateAuthenticationHelper should have been linked out"); + } } } } From 5496a147e6de7ad2df830cb87ba417fd49b17c4d Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 20 Jun 2022 14:01:20 +0200 Subject: [PATCH 12/33] Try fixing failing tests by updating the csproj --- .../Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index f1dd4107a3a..50b0ff03f4c 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -20,6 +20,8 @@ <_MonoAndroidTestPackage>Mono.Android.NET_Tests -$(TestsFlavor)NET6 IL2037 + + true - true - + + + diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index e7d39109585..fa41ec9a92c 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Http; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Xamarin.Android.Net; @@ -13,12 +14,17 @@ public sealed class AndroidMessageHandlerNegotiateAuthenticationTests [SetUp] public void SetUp () { + // this only works in Debug mode because in Release the property is overridden by the linker AppContext.SetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", true); } [Test] public async Task RequestWithoutCredentialsFails () { + if (!NegotiateAuthenticationIsEnabled) { + Assert.Ignore ("Negotiate authentication is not enabled."); + } + using var server = new FakeNtlmServer (port: 47662); var handler = new AndroidMessageHandler (); var client = new HttpClient (handler); @@ -32,6 +38,10 @@ public async Task RequestWithoutCredentialsFails () [Test] public async Task RequestWithCredentialsSucceeds () { + if (!NegotiateAuthenticationIsEnabled) { + Assert.Ignore ("Negotiate authentication is not enabled."); + } + using var server = new FakeNtlmServer (port: 47663); var cache = new CredentialCache (); cache.Add (server.Uri, "NTLM", FakeNtlmServer.Credentials); @@ -45,6 +55,9 @@ public async Task RequestWithCredentialsSucceeds () Assert.AreEqual (FakeNtlmServer.SecretContent, content); } + static bool NegotiateAuthenticationIsEnabled => + typeof (AndroidMessageHandler).GetProperty ("NegotiateAuthenticationIsEnabled", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue (null) as bool? ?? false; + sealed class FakeNtlmServer : IDisposable { public static readonly NetworkCredential Credentials = new NetworkCredential ("User", "Password", "Domain"); From c8b5172e78c00db4691c76a77d18d1768713b83a Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 28 Jun 2022 13:50:33 +0200 Subject: [PATCH 16/33] Remove broken linker test --- .../Tasks/LinkerTests.cs | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 4b064f92ee6..7e2fa8b8fd8 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -468,38 +468,5 @@ public void TypeRegistrationsFallback ([Values (true, false)] bool enabled) } } } - - [Test] - public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool? useNegotiateAuthentication) - { - if (!Builder.UseDotNet) - Assert.Ignore ("Test only valid on .NET 6"); - - var proj = new XamarinAndroidApplicationProject { IsRelease = true }; - proj.AddReferences ("System.Net.Http"); - proj.MainActivity = proj.DefaultMainActivity.Replace ( - "base.OnCreate (bundle);", - "base.OnCreate (bundle);\n" + - "var client = new System.Net.Http.HttpClient (new Xamarin.Android.Net.AndroidMessageHandler ());\n" + - "client.GetAsync (\"https://microsoft.com\").GetAwaiter ().GetResult ();"); - - if (useNegotiateAuthentication.HasValue) - proj.SetProperty ("AndroidUseNegotiateAuthentication", useNegotiateAuthentication.ToString ()); - - using (var b = CreateApkBuilder ()) { - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - var assemblyPath = BuildTest.GetLinkedPath (b, true, "Mono.Android.dll"); - using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) { - Assert.IsTrue (assembly != null); - - var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NegotiateAuthenticationHelper"); - if (useNegotiateAuthentication.HasValue && useNegotiateAuthentication.Value) { - Assert.IsNotNull (td, "NegotiateAuthenticationHelper shouldn't have been linked out"); - } else { - Assert.IsNull (td, "NegotiateAuthenticationHelper should have been linked out"); - } - } - } - } } } From 9f9af1f78eedd75b689609b6ef0ba16eed128c03 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 29 Jun 2022 18:40:26 +0200 Subject: [PATCH 17/33] Fix trimming --- .../AndroidMessageHandler.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index 06a5ac9eeb0..30c1ab3bc43 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -336,22 +336,31 @@ string EncodeUrl (Uri url) /// Task in which the request is executed /// Request provided by /// Cancellation token. - protected override async Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) + protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) { - var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); - #if !MONOANDROID1_0 if (NegotiateAuthenticationIsEnabled) { - if (RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var authData, out var credentials)) { - var authenticatedResponse = await NegotiateAuthenticationHelper.SendWithAuthAsync (this, request, authData, credentials, cancellationToken).ConfigureAwait (false); - if (authenticatedResponse != null) - return authenticatedResponse; - } + return SendWithNegotiateAuthenticationAsync (request, cancellationToken); } #endif + return DoSendAsync (request, cancellationToken); + } + +#if !MONOANDROID1_0 + async Task SendWithNegotiateAuthenticationAsync (HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); + + if (RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var authData, out var credentials)) { + var authenticatedResponse = await NegotiateAuthenticationHelper.SendWithAuthAsync (this, request, authData, credentials, cancellationToken).ConfigureAwait (false); + if (authenticatedResponse != null) + return authenticatedResponse; + } + return response; } +#endif internal async Task DoSendAsync (HttpRequestMessage request, CancellationToken cancellationToken) { From 7fa44501a1ec28abfb8ab604f1c9dd81a0f7780c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 30 Jun 2022 14:03:11 +0200 Subject: [PATCH 18/33] Try enabling tests in Release mode --- .../Mono.Android.NET-Tests.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index da612dec650..a586263e71b 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -40,8 +40,7 @@ - - + From 9f2cf9895d64e199603a5489274c80cd33d74b15 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 30 Jun 2022 14:03:15 +0200 Subject: [PATCH 19/33] Revert "Remove broken linker test" This reverts commit c8b5172e78c00db4691c76a77d18d1768713b83a. --- .../Tasks/LinkerTests.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 7e2fa8b8fd8..4b064f92ee6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -468,5 +468,38 @@ public void TypeRegistrationsFallback ([Values (true, false)] bool enabled) } } } + + [Test] + public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool? useNegotiateAuthentication) + { + if (!Builder.UseDotNet) + Assert.Ignore ("Test only valid on .NET 6"); + + var proj = new XamarinAndroidApplicationProject { IsRelease = true }; + proj.AddReferences ("System.Net.Http"); + proj.MainActivity = proj.DefaultMainActivity.Replace ( + "base.OnCreate (bundle);", + "base.OnCreate (bundle);\n" + + "var client = new System.Net.Http.HttpClient (new Xamarin.Android.Net.AndroidMessageHandler ());\n" + + "client.GetAsync (\"https://microsoft.com\").GetAwaiter ().GetResult ();"); + + if (useNegotiateAuthentication.HasValue) + proj.SetProperty ("AndroidUseNegotiateAuthentication", useNegotiateAuthentication.ToString ()); + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + var assemblyPath = BuildTest.GetLinkedPath (b, true, "Mono.Android.dll"); + using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) { + Assert.IsTrue (assembly != null); + + var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NegotiateAuthenticationHelper"); + if (useNegotiateAuthentication.HasValue && useNegotiateAuthentication.Value) { + Assert.IsNotNull (td, "NegotiateAuthenticationHelper shouldn't have been linked out"); + } else { + Assert.IsNull (td, "NegotiateAuthenticationHelper should have been linked out"); + } + } + } + } } } From a588024b43311ae0661f9214d0eafed5ff61d506 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 30 Jun 2022 14:20:49 +0200 Subject: [PATCH 20/33] Add rudimentary documentation for the feature flag --- Documentation/guides/building-apps/build-process.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/guides/building-apps/build-process.md b/Documentation/guides/building-apps/build-process.md index 42e4837ed9a..ab443746214 100644 --- a/Documentation/guides/building-apps/build-process.md +++ b/Documentation/guides/building-apps/build-process.md @@ -228,3 +228,9 @@ importing *Xamarin.Android.CSharp.targets*: ``` This file can easily be adapted for other languages. + +## Available Feature Switches + +| MSBuild Property Name | AppContext Setting | Description | +|-|-|-| +| AndroidUseNegotiateAuthentication | Xamarin.Android.Net.UseNegotiateAuthentication | Enables NTLM and Negotiate authentication in `AndroidMessageHandler` when set to true | From c793520dfb2f240b463397f89b41968a6ff43d93 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 30 Jun 2022 17:13:06 +0200 Subject: [PATCH 21/33] Remove outdated comment --- .../AndroidMessageHandlerNegotiateAuthenticationTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index fa41ec9a92c..594a4c344ab 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -14,7 +14,6 @@ public sealed class AndroidMessageHandlerNegotiateAuthenticationTests [SetUp] public void SetUp () { - // this only works in Debug mode because in Release the property is overridden by the linker AppContext.SetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", true); } From 8d8ee9b4b98b484732cfb1ff26150ac5be3cada8 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 6 Jul 2022 00:01:40 +0200 Subject: [PATCH 22/33] Use the new NegotiateAuthentication API --- .../AndroidMessageHandler.cs | 40 ++-- .../NegotiateAuthenticationHelper.cs | 222 ++++++------------ 2 files changed, 94 insertions(+), 168 deletions(-) diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index 30c1ab3bc43..c681e73f1c9 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -202,7 +202,7 @@ public int MaxAutomaticRedirections /// /// The pre authentication data. public AuthenticationData? PreAuthenticationData { get; set; } - + /// /// If the website requires authentication, this property will contain data about each scheme supported /// by the server after the response. Note that unauthorized request will return a valid response - you @@ -234,12 +234,12 @@ public int MaxAutomaticRedirections /// /// /// If the request is to the server protected with a self-signed (or otherwise untrusted) SSL certificate, the request will - /// fail security chain verification unless the application provides either the CA certificate of the entity which issued the + /// fail security chain verification unless the application provides either the CA certificate of the entity which issued the /// server's certificate or, alternatively, provides the server public key. Whichever the case, the certificate(s) must be stored /// in this property in order for AndroidMessageHandler to configure the request to accept the server certificate. - /// AndroidMessageHandler uses a custom and to configure the connection. + /// AndroidMessageHandler uses a custom and to configure the connection. /// If, however, the application requires finer control over the SSL configuration (e.g. it implements its own TrustManager) then - /// it should leave this property empty and instead derive a custom class from AndroidMessageHandler and override, as needed, the + /// it should leave this property empty and instead derive a custom class from AndroidMessageHandler and override, as needed, the /// , and methods /// instead /// @@ -352,8 +352,8 @@ string EncodeUrl (Uri url) { var response = await DoSendAsync (request, cancellationToken).ConfigureAwait (false); - if (RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var authData, out var credentials)) { - var authenticatedResponse = await NegotiateAuthenticationHelper.SendWithAuthAsync (this, request, authData, credentials, cancellationToken).ConfigureAwait (false); + if (RequestNeedsAuthorization && NegotiateAuthenticationHelper.RequestNeedsNegotiateAuthentication (this, request, out var requestedAuth)) { + var authenticatedResponse = await NegotiateAuthenticationHelper.SendWithAuthAsync (this, request, requestedAuth, cancellationToken).ConfigureAwait (false); if (authenticatedResponse != null) return authenticatedResponse; } @@ -367,7 +367,7 @@ string EncodeUrl (Uri url) AssertSelf (); if (request == null) throw new ArgumentNullException (nameof (request)); - + if (!request.RequestUri.IsAbsoluteUri) throw new ArgumentException ("Must represent an absolute URI", "request"); @@ -664,7 +664,7 @@ internal Task WriteRequestContentToOutputInternal (HttpRequestMessage request, H return ret; } - HttpContent GetErrorContent (HttpURLConnection httpConnection, HttpContent fallbackContent) + HttpContent GetErrorContent (HttpURLConnection httpConnection, HttpContent fallbackContent) { var contentStream = httpConnection.ErrorStream; @@ -827,7 +827,7 @@ void CollectAuthInfo (HttpHeaderValueCollection head RequestedAuthentication = authData.AsReadOnly (); } - + AuthenticationScheme GetAuthScheme (string scheme) { if (String.Compare ("basic", scheme, StringComparison.OrdinalIgnoreCase) == 0) @@ -882,7 +882,7 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response /// /// Configure the before the request is sent. This method is meant to be overriden /// by applications which need to perform some extra configuration steps on the connection. It is called with all - /// the request headers set, pre-authentication performed (if applicable) but before the request body is set + /// the request headers set, pre-authentication performed (if applicable) but before the request body is set /// (e.g. for POST requests). The default implementation in AndroidMessageHandler does nothing. /// /// Request data @@ -890,7 +890,7 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnection conn) { AssertSelf (); - + return Task.CompletedTask; } @@ -936,9 +936,9 @@ internal Task SetupRequestInternal (HttpRequestMessage request, HttpURLConnectio /// /// Create and configure an instance of . The parameter is set to the /// return value of the method, so it might be null if the application overrode the method and provided - /// no key store. It will not be null when the default implementation is used. The application can return null from this + /// no key store. It will not be null when the default implementation is used. The application can return null from this /// method in which case AndroidMessageHandler will create its own instance of the trust manager factory provided that the - /// list contains at least one valid certificate. If there are no valid certificates and this method returns null, no custom + /// list contains at least one valid certificate. If there are no valid certificates and this method returns null, no custom /// trust manager will be created since that would make all the HTTPS requests fail. /// /// The trust manager factory. @@ -961,7 +961,7 @@ void AppendEncoding (string encoding, ref List ? list) return; list.Add (encoding); } - + async Task SetupRequestInternal (HttpRequestMessage request, URLConnection conn) { if (conn == null) @@ -982,7 +982,7 @@ void AppendEncoding (string encoding, ref List ? list) if (request.Content != null) AddHeaders (httpConnection, request.Content.Headers); AddHeaders (httpConnection, request.Headers); - + List ? accept_encoding = null; decompress_here = false; @@ -990,7 +990,7 @@ void AppendEncoding (string encoding, ref List ? list) AppendEncoding (GZIP_ENCODING, ref accept_encoding); decompress_here = true; } - + if ((AutomaticDecompression & DecompressionMethods.Deflate) != 0) { AppendEncoding (DEFLATE_ENCODING, ref accept_encoding); decompress_here = true; @@ -1009,7 +1009,7 @@ void AppendEncoding (string encoding, ref List ? list) if (!String.IsNullOrEmpty (cookieHeaderValue)) httpConnection.SetRequestProperty ("Cookie", cookieHeaderValue); } - + HandlePreAuthentication (httpConnection); await SetupRequest (request, httpConnection).ConfigureAwait (continueOnCapturedContext: false);; SetupRequestBody (httpConnection, request); @@ -1066,7 +1066,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe // there is no point in changing the behavior of the default SSL socket factory if (!gotCerts && _callbackTrustManagerHelper == null) return; - + tmf = TrustManagerFactory.GetInstance (TrustManagerFactory.DefaultAlgorithm); tmf?.Init (gotCerts ? keyStore : null); // only use the custom key store if the user defined any trusted certs } @@ -1099,7 +1099,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe return keyStore; } } - + void HandlePreAuthentication (HttpURLConnection httpConnection) { var data = PreAuthenticationData; @@ -1145,7 +1145,7 @@ void AddHeaders (HttpURLConnection conn, HttpHeaders headers) conn.SetRequestProperty (header.Key, header.Value != null ? String.Join (GetHeaderSeparator (header.Key), header.Value) : String.Empty); } } - + void SetupRequestBody (HttpURLConnection httpConnection, HttpRequestMessage request) { if (request.Content == null) { diff --git a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs index 40b8da4181d..caf1d19e460 100644 --- a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs +++ b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs @@ -1,12 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Reflection; -using System.Security.Authentication.ExtendedProtection; +using System.Net.Security; using System.Threading; using System.Threading.Tasks; @@ -17,51 +16,55 @@ internal static class NegotiateAuthenticationHelper { const int MaxRequests = 10; + internal class RequestedNegotiateAuthenticationData + { + public string AuthType { get; init; } + public bool IsProxyAuth { get; init; } + public NetworkCredential Credential { get; init; } + } + internal static bool RequestNeedsNegotiateAuthentication ( AndroidMessageHandler handler, HttpRequestMessage request, - [NotNullWhen (true)] out AuthenticationData? requestedAuthentication, - [NotNullWhen (true)] out NetworkCredential? correspondingCredentials) + [NotNullWhen (true)] out RequestedNegotiateAuthenticationData? requestedAuth) { - IEnumerable authenticationData = handler.RequestedAuthentication ?? Enumerable.Empty (); + requestedAuth = null; + + IEnumerable authenticationData = handler.RequestedAuthentication ?? Array.Empty (); foreach (var auth in authenticationData) { if (TryGetSupportedAuthType (auth.Challenge, out var authType)) { var credentials = auth.UseProxyAuthentication ? handler.Proxy?.Credentials : handler.Credentials; - correspondingCredentials = credentials?.GetCredential (request.RequestUri, authType); + var correspondingCredential = credentials?.GetCredential (request.RequestUri, authType); + + if (correspondingCredential != null) { + requestedAuth = new RequestedNegotiateAuthenticationData { + IsProxyAuth = auth.UseProxyAuthentication, + AuthType = authType, + Credential = correspondingCredential + }; - if (correspondingCredentials != null) { - requestedAuthentication = auth; return true; } } } - requestedAuthentication = null; - correspondingCredentials = null; return false; } internal static async Task SendWithAuthAsync ( AndroidMessageHandler handler, HttpRequestMessage request, - AuthenticationData requestedAuthentication, - NetworkCredential credentials, + RequestedNegotiateAuthenticationData requestedAuth, CancellationToken cancellationToken) { - if (!TryGetSupportedAuthType (requestedAuthentication.Challenge, out var authType)) { - return null; - } - - var isProxyAuth = requestedAuthentication.UseProxyAuthentication; - var spn = await GetSpn (handler, request, isProxyAuth, cancellationToken).ConfigureAwait (false); - // TODO: replace with NegotiateAuthentication once it's available in dotnet/runtime - var authContext = new NTAuthentication ( - isServer: false, - authType, - credentials, - spn: spn, - requestedContextFlags: GetRequestedContextFlags (isProxyAuth), - channelBinding: null); + using var authContext = new NegotiateAuthentication ( + new NegotiateAuthenticationClientOptions { + Package = requestedAuth.AuthType, + Credential = requestedAuth.Credential, + TargetName = await GetTargetName (handler, request, requestedAuth.IsProxyAuth, cancellationToken).ConfigureAwait (false), + RequiredProtectionLevel = requestedAuth.IsProxyAuth ? ProtectionLevel.None : ProtectionLevel.Sign, + } + ); // we need to make sure that the handler doesn't override the authorization header // with the user defined pre-authentication data @@ -69,10 +72,9 @@ internal static class NegotiateAuthenticationHelper handler.PreAuthenticate = false; try { - return await DoSendWithAuthAsync (handler, request, authContext, authType, isProxyAuth, cancellationToken); + return await DoSendWithAuthAsync (handler, request, authContext, requestedAuth, cancellationToken); } finally { handler.PreAuthenticate = originalPreAuthenticate; - authContext.CloseContext (); } } @@ -88,53 +90,51 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) static async Task DoSendWithAuthAsync ( AndroidMessageHandler handler, HttpRequestMessage request, - NTAuthentication authContext, - string authType, - bool isProxyAuth, + NegotiateAuthentication authContext, + RequestedNegotiateAuthenticationData requestedAuth, CancellationToken cancellationToken) { HttpResponseMessage? response = null; - string? challenge = null; int requestCounter = 0; + bool needDrain = false; // TODO maybe we need to drain even the first response on Android? + string? challengeData = null; while (requestCounter++ < MaxRequests) { - var challengeResponse = authContext.GetOutgoingBlob (challenge); - if (challengeResponse == null) { - // response indicated denial even after login, so stop processing - // and return current response + var challengeResponse = authContext.GetOutgoingBlob (challengeData, out var statusCode); + + if (challengeResponse is null || statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded) { + // Response indicated denial even after login, so stop processing and return current response. break; } - var headerValue = new AuthenticationHeaderValue (authType, challengeResponse); - if (isProxyAuth) { - request.Headers.ProxyAuthorization = headerValue; - } else { - request.Headers.Authorization = headerValue; + if (needDrain) { + // We need to drain the content otherwise the next request + // won't reuse the same TCP socket and persistent auth won't work. + await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); } + SetAuthorizationHeader (request, requestedAuth, challengeResponse); response = await handler.DoSendAsync (request, cancellationToken).ConfigureAwait (false); - // we need to drain the content otherwise the next request - // won't reuse the same TCP socket and persistent auth won't work - await response.Content.LoadIntoBufferAsync ().ConfigureAwait (false); - - if (authContext.IsCompleted || !TryGetChallenge (response, authType, isProxyAuth, out challenge)) { + if (authContext.IsAuthenticated || !TryGetChallenge (response, requestedAuth, out challengeData)) { break; } - if (!IsAuthenticationChallenge (response, isProxyAuth)) { - // Tail response for Negotiate on successful authentication. - // Validate it before we proceed. - authContext.GetOutgoingBlob (challenge); + if (!IsAuthenticationChallenge (response, requestedAuth)) + { + // Tail response for Negotiate on successful authentication. Validate it before we proceed. + authContext.GetOutgoingBlob(challengeData, out _); break; } + + needDrain = true; } return response; } - static async Task GetSpn ( + static async Task GetTargetName ( AndroidMessageHandler handler, HttpRequestMessage request, bool isProxyAuth, @@ -174,110 +174,36 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) } } - static int GetRequestedContextFlags (bool isProxyAuth) + static void SetAuthorizationHeader (HttpRequestMessage request, RequestedNegotiateAuthenticationData requestedAuth, string challengeResponse) { - // the ContextFlagsPal is internal type in dotnet/runtime and we can't - // use it directly here so we have to use ints directly - int contextFlags = 0x00000800; // ContextFlagsPal.Connection - - // When connecting to proxy server don't enforce the integrity to avoid - // compatibility issues. The assumption is that the proxy server comes - // from a trusted source. - if (!isProxyAuth) { - contextFlags |= 0x00010000; // ContextFlagsPal.InitIntegrity + var headerValue = new AuthenticationHeaderValue (requestedAuth.AuthType, challengeResponse); + if (requestedAuth.IsProxyAuth) { + request.Headers.ProxyAuthorization = headerValue; + } else { + request.Headers.Authorization = headerValue; } - - return contextFlags; } - static bool TryGetChallenge (HttpResponseMessage response, string authType, bool isProxyAuth, [NotNullWhen (true)] out string? challenge) + static bool TryGetChallenge (HttpResponseMessage? response, RequestedNegotiateAuthenticationData requestedAuth, [NotNullWhen (true)] out string? challengeData) { - var responseHeaderValues = isProxyAuth ? response.Headers.ProxyAuthenticate : response.Headers.WwwAuthenticate; - challenge = responseHeaderValues?.FirstOrDefault (headerValue => headerValue.Scheme == authType)?.Parameter; - return !string.IsNullOrEmpty (challenge); + challengeData = null; + + var responseHeaderValues = requestedAuth.IsProxyAuth ? response?.Headers.ProxyAuthenticate : response?.Headers.WwwAuthenticate; + if (responseHeaderValues is not null) { + foreach (var headerValue in responseHeaderValues) { + if (headerValue.Scheme == requestedAuth.AuthType) { + challengeData = headerValue.Parameter; + break; + } + } + } + + return !string.IsNullOrEmpty (challengeData); } - static bool IsAuthenticationChallenge (HttpResponseMessage response, bool isProxyAuth) - => isProxyAuth + static bool IsAuthenticationChallenge (HttpResponseMessage response, RequestedNegotiateAuthenticationData requestedAuth) + => requestedAuth.IsProxyAuth ? response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired : response.StatusCode == HttpStatusCode.Unauthorized; - - // This class will be removed once the new NegotiateAuthentication class is available in dotnet/runtime - private sealed class NTAuthentication - { - const string AssemblyName = "System.Net.Http"; - const string TypeName = "System.Net.NTAuthentication"; - const string ContextFlagsPalTypeName = "System.Net.ContextFlagsPal"; - - const string ConstructorDescription = "#ctor(System.Boolean,System.String,System.Net.NetworkCredential,System.String,System.Net.ContextFlagsPal,System.Security.Authentication.ExtendedProtection.ChannelBinding)"; - const string IsCompletedPropertyName = "IsCompleted"; - const string GetOutgoingBlobMethodName = "GetOutgoingBlob"; - const string CloseContextMethodName = "CloseContext"; - - const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - - static Lazy s_NTAuthenticationType = new (() => FindType (TypeName, AssemblyName)); - static Lazy s_NTAuthenticationConstructorInfo = new (() => GetNTAuthenticationConstructor ()); - static Lazy s_IsCompletedPropertyInfo = new (() => GetProperty (IsCompletedPropertyName)); - static Lazy s_GetOutgoingBlobMethodInfo = new (() => GetMethod (GetOutgoingBlobMethodName)); - static Lazy s_CloseContextMethodInfo = new (() => GetMethod (CloseContextMethodName)); - - static Type FindType (string typeName, string assemblyName) - => Type.GetType ($"{typeName}, {assemblyName}", throwOnError: true)!; - - static ConstructorInfo GetNTAuthenticationConstructor () - { - var contextFlagsPalType = FindType (ContextFlagsPalTypeName, AssemblyName); - var paramTypes = new[] { - typeof (bool), - typeof (string), - typeof (NetworkCredential), - typeof (string), - contextFlagsPalType, - typeof (ChannelBinding) - }; - - return s_NTAuthenticationType.Value.GetConstructor (InstanceBindingFlags, paramTypes) - ?? throw new MissingMemberException (TypeName, ConstructorInfo.ConstructorName); - } - - static PropertyInfo GetProperty (string name) - => s_NTAuthenticationType.Value.GetProperty (name, InstanceBindingFlags) - ?? throw new MissingMemberException (TypeName, name); - - static MethodInfo GetMethod (string name) - => s_NTAuthenticationType.Value.GetMethod (name, InstanceBindingFlags) - ?? throw new MissingMemberException (TypeName, name); - - object _instance; - - [DynamicDependency (ConstructorDescription, TypeName, AssemblyName)] - internal NTAuthentication ( - bool isServer, - string package, - NetworkCredential credential, - string? spn, - int requestedContextFlags, - ChannelBinding? channelBinding) - { - var constructorParams = new object?[] { isServer, package, credential, spn, requestedContextFlags, channelBinding }; - _instance = s_NTAuthenticationConstructorInfo.Value.Invoke (constructorParams); - } - - public bool IsCompleted - => GetIsCompleted (); - - [DynamicDependency ($"get_{IsCompletedPropertyName}", TypeName, AssemblyName)] - bool GetIsCompleted () - => (bool)(s_IsCompletedPropertyInfo.Value.GetValue (_instance) ?? false); - - [DynamicDependency (GetOutgoingBlobMethodName, TypeName, AssemblyName)] - public string? GetOutgoingBlob (string? incomingBlob) - => (string?)s_GetOutgoingBlobMethodInfo.Value.Invoke (_instance, new object?[] { incomingBlob }); - - [DynamicDependency (CloseContextMethodName, TypeName, AssemblyName)] - public void CloseContext () - => s_CloseContextMethodInfo.Value.Invoke (_instance, null); - } } } From d6ecdd7da80f77476d09e2531c56804146df6621 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 7 Jul 2022 14:48:40 +0200 Subject: [PATCH 23/33] Address review --- .../Xamarin.Android.Net/NegotiateAuthenticationHelper.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs index caf1d19e460..261a4841e37 100644 --- a/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs +++ b/src/Mono.Android/Xamarin.Android.Net/NegotiateAuthenticationHelper.cs @@ -124,7 +124,11 @@ static bool TryGetSupportedAuthType (string challenge, out string authType) if (!IsAuthenticationChallenge (response, requestedAuth)) { // Tail response for Negotiate on successful authentication. Validate it before we proceed. - authContext.GetOutgoingBlob(challengeData, out _); + authContext.GetOutgoingBlob(challengeData, out statusCode); + if (statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded) + { + throw new HttpRequestException($"Authentication validation failed with error - {statusCode}.", null, HttpStatusCode.Unauthorized); + } break; } From fb90144283e621beff2c344ad4163373d7dc7d7a Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 19 Jul 2022 13:23:38 +0200 Subject: [PATCH 24/33] Move auth tests to the shared project --- .../Mono.Android-Test.Shared.projitems | 1 + .../Mono.Android.NET-Tests.csproj | 2 -- ...sageHandlerNegotiateAuthenticationTests.cs | 31 ++++++++++++++----- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems b/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems index 9881054e160..25fdfb8293a 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems +++ b/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems @@ -48,6 +48,7 @@ + diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index a586263e71b..22d9dce4925 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -39,8 +39,6 @@ - - diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index 594a4c344ab..0d06982fe6a 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -11,17 +11,35 @@ namespace Xamarin.Android.NetTests { [TestFixture] public sealed class AndroidMessageHandlerNegotiateAuthenticationTests { + // Negotiate authentication is available for Android since .NET 7 + public static bool ShouldBeAvailable => Environment.Version.Major >= 7; + [SetUp] - public void SetUp () + public void EnableNegotiateAuthentication () { AppContext.SetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", true); } + [Test] + public void NegotiateAuthenticationIsEnabled () + { + const string propertyName = "NegotiateAuthenticationIsEnabled"; + var property = typeof (AndroidMessageHandler).GetProperty (propertyName, BindingFlags.NonPublic | BindingFlags.Static); + if (!ShouldBeAvailable) { + Assert.IsNull (property, $"The {nameof (AndroidMessageHandler)}.{propertyName} property exists in the Monodroid build"); + } else { + Assert.IsNotNull (property, $"The {nameof (AndroidMessageHandler)}.{propertyName} property is missing in the .NET build"); + + var isEnabled = property.GetValue (null) as bool? ?? false; + Assert.IsTrue (isEnabled, "Negotiate authentication is not enabled"); + } + } + [Test] public async Task RequestWithoutCredentialsFails () { - if (!NegotiateAuthenticationIsEnabled) { - Assert.Ignore ("Negotiate authentication is not enabled."); + if (!ShouldBeAvailable) { + Assert.Ignore ("Negotiate authentication is only available in .NET 7+"); } using var server = new FakeNtlmServer (port: 47662); @@ -37,8 +55,8 @@ public async Task RequestWithoutCredentialsFails () [Test] public async Task RequestWithCredentialsSucceeds () { - if (!NegotiateAuthenticationIsEnabled) { - Assert.Ignore ("Negotiate authentication is not enabled."); + if (!ShouldBeAvailable) { + Assert.Ignore ("Negotiate authentication is only available in .NET 7+"); } using var server = new FakeNtlmServer (port: 47663); @@ -54,9 +72,6 @@ public async Task RequestWithCredentialsSucceeds () Assert.AreEqual (FakeNtlmServer.SecretContent, content); } - static bool NegotiateAuthenticationIsEnabled => - typeof (AndroidMessageHandler).GetProperty ("NegotiateAuthenticationIsEnabled", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue (null) as bool? ?? false; - sealed class FakeNtlmServer : IDisposable { public static readonly NetworkCredential Credentials = new NetworkCredential ("User", "Password", "Domain"); From c109b5f9983184678b1936b1a0de0838a8fcb72e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 19 Jul 2022 14:49:06 +0200 Subject: [PATCH 25/33] WIP: update linker tests --- .../Tasks/LinkerTests.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 4b064f92ee6..55165cc5c58 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -7,6 +7,7 @@ using MonoDroid.Tuner; using NUnit.Framework; using Xamarin.ProjectTools; +using SR = System.Reflection; namespace Xamarin.Android.Build.Tests { @@ -473,7 +474,7 @@ public void TypeRegistrationsFallback ([Values (true, false)] bool enabled) public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool? useNegotiateAuthentication) { if (!Builder.UseDotNet) - Assert.Ignore ("Test only valid on .NET 6"); + Assert.Ignore ("Test only valid on .NET"); var proj = new XamarinAndroidApplicationProject { IsRelease = true }; proj.AddReferences ("System.Net.Http"); @@ -486,19 +487,29 @@ public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool if (useNegotiateAuthentication.HasValue) proj.SetProperty ("AndroidUseNegotiateAuthentication", useNegotiateAuthentication.ToString ()); + var shouldBeEnabled = useNegotiateAuthentication.HasValue && useNegotiateAuthentication.Value; using (var b = CreateApkBuilder ()) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); var assemblyPath = BuildTest.GetLinkedPath (b, true, "Mono.Android.dll"); + using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) { Assert.IsTrue (assembly != null); var td = assembly.MainModule.GetType ("Xamarin.Android.Net.NegotiateAuthenticationHelper"); - if (useNegotiateAuthentication.HasValue && useNegotiateAuthentication.Value) { + if (shouldBeEnabled) { Assert.IsNotNull (td, "NegotiateAuthenticationHelper shouldn't have been linked out"); } else { Assert.IsNull (td, "NegotiateAuthenticationHelper should have been linked out"); } } + +#if NETCOREAPP + var androidMessageHandler = SR.Assembly.Load (assemblyPath).GetType ("Xamarin.Android.Net.AndroidMessageHandler"); + var property = SR.IntrospectionExtensions.GetTypeInfo (androidMessageHandler)?.GetProperty ("NegotiateAuthenticationIsEnabled", SR.BindingFlags.NonPublic | SR.BindingFlags.Static); + var isEnabled = property?.GetValue (null) as bool? ?? false; + + Assert.AreEqual (shouldBeEnabled, isEnabled); +#endif } } } From d0656c62d41df9e3e94d4e8d04ed8a8844b736df Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 19 Jul 2022 14:57:28 +0200 Subject: [PATCH 26/33] Update property comment --- .../Xamarin.Android.Net/AndroidMessageHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index c681e73f1c9..439a9083e40 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -265,6 +265,11 @@ public int MaxAutomaticRedirections public TimeSpan ReadTimeout { get; set; } = TimeSpan.FromHours (24); #if !MONOANDROID1_0 + /// + /// A feature switch that determines whether the message handler should attempt to authenticate the user + /// using the NTLM/Negotiate authentication method. Enable the feature by adding + /// true to your project file. + /// static bool NegotiateAuthenticationIsEnabled => AppContext.TryGetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", out bool isEnabled) && isEnabled; #endif From 74a92480e8b5c0ac267ca1e1b7a12f383100b298 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 20 Jul 2022 11:19:08 +0200 Subject: [PATCH 27/33] Update docs --- Documentation/guides/building-apps/build-process.md | 6 ------ Documentation/guides/building-apps/build-properties.md | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/guides/building-apps/build-process.md b/Documentation/guides/building-apps/build-process.md index ab443746214..42e4837ed9a 100644 --- a/Documentation/guides/building-apps/build-process.md +++ b/Documentation/guides/building-apps/build-process.md @@ -228,9 +228,3 @@ importing *Xamarin.Android.CSharp.targets*: ``` This file can easily be adapted for other languages. - -## Available Feature Switches - -| MSBuild Property Name | AppContext Setting | Description | -|-|-|-| -| AndroidUseNegotiateAuthentication | Xamarin.Android.Net.UseNegotiateAuthentication | Enables NTLM and Negotiate authentication in `AndroidMessageHandler` when set to true | diff --git a/Documentation/guides/building-apps/build-properties.md b/Documentation/guides/building-apps/build-properties.md index 843d9871c23..eb1c02ad601 100644 --- a/Documentation/guides/building-apps/build-properties.md +++ b/Documentation/guides/building-apps/build-properties.md @@ -1328,6 +1328,12 @@ than `aapt`. Added in Xamarin.Android 8.1. +## AndroidUseNegotiateAuthentication + +A boolean property which enables support for NTLM/Negotiate authentication in `AndroidMessageHandler`. The feature is disabled by default. + +Support for this property was added in .NET 7 and has no effect in "legacy" Xamarin.Android. + ## AndroidUseSharedRuntime A boolean property that From 9c9aa272640d129fdaf3845687671eb9a95ab1d1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 20 Jul 2022 11:19:32 +0200 Subject: [PATCH 28/33] Fall back to class from resource --- ...sageHandlerNegotiateAuthenticationTests.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index 0d06982fe6a..ec1dfd5dc10 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Net; using System.Net.Http; @@ -134,13 +136,24 @@ static FakeResponse Handle (string? authorization) _ => new (HttpStatusCode.Unauthorized, ntlm, string.Empty) }; - record FakeResponse (HttpStatusCode statusCode, string? header, string body) + class FakeResponse { + private HttpStatusCode _statusCode; + private string? _header; + private string _body; + + public FakeResponse (HttpStatusCode statusCode, string? header, string body) + { + _statusCode = statusCode; + _header = header; + _body = body; + } + public void ConfigureAndClose (HttpListenerResponse res) { - res.StatusCode = (int)statusCode; - if (header != null) res.AddHeader ("WWW-Authenticate", header); - res.Close (Encoding.UTF8.GetBytes (body), false); + res.StatusCode = (int)_statusCode; + if (header != null) res.AddHeader ("WWW-Authenticate", _header); + res.Close (Encoding.UTF8.GetBytes (_body), false); } } } From c3358f09748699e0cc9521aed23172713f5319cf Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 20 Jul 2022 11:25:12 +0200 Subject: [PATCH 29/33] Add comment to clarify usage of SetSwitch at runtime --- .../AndroidMessageHandlerNegotiateAuthenticationTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index ec1dfd5dc10..2ecc54cd0d0 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -19,6 +19,9 @@ public sealed class AndroidMessageHandlerNegotiateAuthenticationTests [SetUp] public void EnableNegotiateAuthentication () { + // It wasn't possible to enable the feature flag using the msbuild $(AndroidUseNegotiateAuthentication) property + // so we're enabling the feature at runtime. This only works in Debug builds (the linker bakes in the boolean + // value in Release builds, see LinkerTests) AppContext.SetSwitch ("Xamarin.Android.Net.UseNegotiateAuthentication", true); } From 58f95210efaa2c327bc06b5de804f5fc0b0f1d00 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 20 Jul 2022 13:27:16 +0200 Subject: [PATCH 30/33] Fix typo --- .../AndroidMessageHandlerNegotiateAuthenticationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs index 2ecc54cd0d0..bd1d9caf886 100644 --- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs +++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerNegotiateAuthenticationTests.cs @@ -155,7 +155,7 @@ public FakeResponse (HttpStatusCode statusCode, string? header, string body) public void ConfigureAndClose (HttpListenerResponse res) { res.StatusCode = (int)_statusCode; - if (header != null) res.AddHeader ("WWW-Authenticate", _header); + if (_header != null) res.AddHeader ("WWW-Authenticate", _header); res.Close (Encoding.UTF8.GetBytes (_body), false); } } From ab5b60b2ee7a6262388e0b1860615b0e4a7d8c41 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 20 Jul 2022 20:45:53 +0200 Subject: [PATCH 31/33] Update setting the property + tests --- .../Microsoft.Android.Sdk.ILLink.targets | 4 ---- ...Microsoft.Android.Sdk.RuntimeConfig.targets | 8 ++++++++ .../Tasks/LinkerTests.cs | 18 +++++++++++------- .../Mono.Android.NET-Tests.csproj | 1 + ...ssageHandlerNegotiateAuthenticationTests.cs | 9 --------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets index d4d8bf6b553..67d1bbba119 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets @@ -28,10 +28,6 @@ This file contains the .NET 5-specific targets to customize ILLink Condition="'$(VSAndroidDesigner)' != ''" Value="$(VSAndroidDesigner)" Trim="true" /> - + + + + + prop.Name == "NegotiateAuthenticationIsEnabled"); + Assert.NotNull (property, "The AndroidMessageHandler.NegotiateAuthenticationIsEnabled property is missing"); + + var instructions = property.GetMethod.Body.Instructions; + Assert.AreEqual (2, instructions.Count); - Assert.AreEqual (shouldBeEnabled, isEnabled); -#endif + // check that the property getter was replaced with `return true` or `return false` + var expectedConstant = shouldBeEnabled ? Mono.Cecil.Cil.Code.Ldloc_1 : Mono.Cecil.Cil.Code.Ldloc_0; + Assert.AreEqual(expectedConstant, instructions[0].OpCode); + Assert.AreEqual(Mono.Cecil.Cil.Code.Ret, instructions[1].OpCode); + } } } } diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index 22d9dce4925..984363d65ee 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -19,6 +19,7 @@ <_MonoAndroidTestPackage>Mono.Android.NET_Tests -$(TestsFlavor)NET6 IL2037 + true