I need to reliably detect if a device has full internet access, i.e. that the user is not confined to a captive portal (also called walled garden), i.e. a limited subnet which forces users to submit their credentials on a form in order to get full access.
My app is automating the authentication process, and therefore it is important to know that full internet access is not available before starting the logon activity.
The question is not about how to check that the network interface is up and in a connected state. It is about making sure the device has unrestricted internet access as opposed to a sandboxed intranet segment.
All the approaches I have tried so far are failing, because connecting to any well-known host would not throw an exception but return a valid HTTP 200
response code because all requests are routed to the login page.
Here are all the approaches I tried but they all return true
instead of false
for the reasons explained above:
1:
InetAddress.getByName(host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>
2:
Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();
3:
URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
So, how do I make sure I connected to an actual host instead of the login redirection page? Obviously, I could check the actual response body from the 'ping' host I use but it does not look like a proper solution.
if you are already using
retrofit
you can do it byretrofit
. just make a ping.html page and send an head request to it using retrofit and make sure your http client is configured like below: (followRedirects(false)
part is the most important part)then build your retrofit like below:
your InternetCheckApi.class would be like:
then you can use it like below:
note that your internet check http client must be separate from your main http client.
This has been implemented on Android 4.2.2+ version - I find their approach fast and interesting :
CaptivePortalTracker.java detects walled garden as follows - Try to connect to www.google.com/generate_204 - Check that the HTTP response is 204
If the check fails, we are in a walled garden.
I believe preventing redirection for your connection will work.
If that doesn't work, then I think the only way to do it is to check the body of the response.
Another possible solution might be to connect via HTTPS and inspect the target certificate. Not sure if walled gardens actually serve the login page via HTTPS or just drop the connections. In either case, you should be able to see that your destination is not the one you expected.
Of course, you also have the overhead of TLS and certificate checks. Such is the price of authenticated connections, unfortunately.
For reference, here is the 'official' method from the Android 4.0.1 AOSP code base: WifiWatchdogStateMachine.isWalledGardenConnection(). I am including the code below just in case the link breaks in the future.
This approach relies on a specific URL,
mWalledGardenUrl = "http://clients3.google.com/generate_204"
always returning a204
response code. This will work even if DNS has been interfered with since in that case a200
code will be returned instead of the expected204
. I have seen some captive portals spoofing requests to this specific URL in order to prevent the Internet not accessible message on Android devices.Google has a variation of this theme: fetching
http://www.google.com/blank.html
will return a200
code with a zero-length response body. So if you get a non-empty body this would be another way to figure out that you are behind a walled garden.Apple has its own URLs for detecting captive portals: when network is up IOS and MacOS devices would connect to an URL like http://www.apple.com/library/test/success.html, http://attwifi.apple.com/library/test/success.html, or http://captive.apple.com/hotspot-detect.html which must return an HTTP status code of
200
and a body containingSuccess
.