Supposed “low-quality” connection

Someone may say this happens only to me because I’m in Cuba, but I like to believe that even in developed countries with ubiquitous Internet access, sometimes you may run into this problem.

The problem is that, since some minor update of Android 5 (and the world just recently got version 7), Android doesn’t like connecting to a WiFi network without Internet access.

This network has no Internet access. Stay connected? Checkbox. Don't ask again for this network. NO YES.

Even if you check “Don’t ask again” above, Android will not reconnect automatically to that “low-quality” connection.

Sometimes I want WiFi without Internet

This is not a rant about Internet access in Cuba. I’m part of Proyecto Delta, a Science/Comedy show run by professors & students of the Faculty of Math & Computer Science every Friday night at a movie theater in Havana.

In that show we keep a local WiFi connection for the audience to participate and for the staff to communicate with each other. And since it’s just internal, it wouldn’t be connected to the Internet even if we could.

More examples come to mind, I could have a bunch of robots controlled by Android devices talking to each other, again over WiFi without necessarily having to have Internet access. And you wouldn’t want Android to refuse to connect to an otherwise perfectly valid network.

The solution

This is a trick, and it may break at anytime, because I’m targeting the specific implementation of connectivity check on Android. And you should always develop for interfaces, NOT implementations. However, I have no “interface” to develop for here, so it is what it is.

Android simply tries URLs like these to decide if the connection has Internet access or not:

http://connectivitycheck.android.com/generate_204
http://connectivitycheck.gstatic.com/generate_204

And it expects them to return HTTP status 204 (No Content). I’m not sure if there are more, but I’ve seen those two from Android 5 & 6.

The solution to make it believe that it has Internet access is to tell your DNS to point those addresses to a host inside your network and then have that host reply 204 to the path /generate_204.

All within DD-WRT

There are many ways to achieve that, but since my WiFi router has DD-WRT, I decided to do it right there. That way I didn’t have to rely on two devices.

First, I told DnsMasq to point that domain to the IP address of the router, like this:

# Change 10.0.0.1 to the IP of your HTTP server
host-record=connectivitycheck.android.com,10.0.0.1
host-record=connectivitycheck.gstatic.com,10.0.0.1

And second, I enabled lighttpd and added the following line to lighttpd.conf:

url.rewrite-once = ( "^/generate_204$" => "generate_204.php" )

And that PHP script, which must be placed somewhere lighttpd can find it, has just this line:

<?php header("HTTP/1.0 204 No Content"); ?>

After these two steps, and turning the WiFi off and then on, Android went from Sad WiFi to Happy WiFi:

From Sad WiFi to Happy WiFi

Gotta say, it’s amazing for DD-WRT to include PHP. I’m not a fan of PHP, I quite hate it and agree that it’s a fractal of bad design and another security concern but it’s a high level language and it was much easier for this problem than writing a server or CGI program in C.

Important

The HTTP server that you point your DNS to must be listening on port 80. That is usually the case, but DD-WRT already uses port 80 for its own web server, and port 81 for lighttpd. What I did was to have DD-WRT’s web server listen only on port 443 –which even with the invalid SSL certificate, I recommend–. And then I set up lighttpd to listen on port 80 to answer for generate_204 and redirect empty requests to https://..., like this:

$HTTP["host"] =~ "(.*)" {
  url.redirect = ( "^/$" => "https://%1/" )
}

Chip in

If you know a simpler way to achieve this with Lighttpd, or want to share a way to do it in a different setup, fire away in the comments below. If I can collect several ways, I’ll add them as a list to the post.