Friday, 14 August 2020

Connecting to localhost over HTTPS for (Xamarin) Android release builds

I've previously written several posts about connecting to localhost HTTPs services from Xamarin apps running on simulators and emulators. Over time this has become easier, and standardised, and my last post outlined the approach to take.

A user recently contacted me to point out that while my solution worked just fine in DEBUG mode, it didn't work in RELEASE mode on Android. This prompted much head scratching by me, and a journey into the heart of darkness, before I realised that I'm an idiot.

So the purpose of this blog post is to outline the solution to the issue, in the hope that others don't go down the rabbit hole I went down. It should also serve as a reminder to myself when I encounter this problem again in future.

Problem

There's a sample app that demos how to connect to localhost HTTPs services from iOS simulators and Android emulators. The idea is to ignore SSL errors on iOS/Android for local HTTPS services by setting the HttpClientHandler.ServerCertificateCustomValidationCallback property to a callback that ignores the result of the certificate security check for the localhost certificate. For more information about this, see Connecting to localhost over HTTPS from simulators and emulators (revisited).

The problem was that while the solution outlined in my previous blog post worked fine in both DEBUG and RELEASE mode in iOS, it only worked in DEBUG mode on Android. When switched to a RELEASE build, the sample app wouldn't retrieve any data from the web service, but provided no indication why not. It was frustrating! Note that both DEBUG and RELEASE modes on Android were set to use the AndroidClientHandler native network stack.

Journey

I convinced myself that the linker was linking out a type/namespace that was required. So I tried the usual trick of ensuring the type was referenced from C# in the Android project. That didn't help. Then I created a custom linker configuration (linker.xml) that specified key types and namespace to preserve. That also didn't help. I even tried turning the linker off. But still the app wouldn't retrieve any data from the localhost HTTPS web service.

I then had a diversion into wondering if it was related to the API level, so tried the app on different API levels. Still nothing.

Googling the issue didn't turn up anything similar. That was bizarre. No one had experienced anything remotely similar??? That made me wonder if it was a new issue, caused by the latest release of Mono. So I started looking at issues in the Mono.Android repo and while I found a few issues that initially looked promising, they were all dead ends.

So then I started to look at the code for the AndroidClientHandler class (in the Mono.Android repo), in the hope of finding an obvious property that needed setting, or method that needed calling. That didn't help either. I was just about to close my browser window when I noticed that AndroidClientHandler catches a number of Java exceptions. That got me thinking that maybe there's a native Java exception bubbling up, that AndroidClientHandler doesn't catch. So I added code to catch all Java exceptions to my Android project. That immediately revealed that a Java.Net.SocketException was occurring in Xamarin.Android.Net.AndroidClientHandler.DoProcessRequest (and wasn't being caught by AndroidClientHandler). The exception said that the socket failed due to an EACCES (permission denied) error.

It couldn't really be that simple could it??? It was.

Solution

Ensure the Android manifest has the internet permission enabled. That should have been the first check I made. Yes, it really was just that.

Note to future Dave: check the Android manifest first next time!

No comments:

Post a comment