Hey Android, Where’s the USB Data?

Does your ping look like this when using Android’s sweet USB tethering feature? I tried lots of things:

  • Disabling/Enabling USB Tethering
  • Disconnecting/Reconnecting the USB cable
  • Restarting the phone
  • Disabling/Enabling the network interface on the computer
  • Restarting the computer

All of this got me exactly no where. The phone is still connected to the interwho, but it’s just not sharing it with my laptop. So sad. So, thinking a little bit more laterally (read as, poking around looking for a sign from the gods), I tried toggling the data access of the phone. This means going to Menu > Wireless & networks > Mobile network settings > Data enabled and unchecking the box.

Why does it make sense to turn off data when what you want is data? Well… it doesn’t. But, if you then turn the data back on, the phone may well realize that it has been fraudulently denying your poor computer all the data goodness the marvelous (if mystifying) Android platform promises.  I say “may well” because with the fragmentation of the Android market and the amount of irresponsible hackery the carriers subject the OS to, this may not actually fix your problem though it fixes mine. I’m using a vanilla-tainted-by-T-Mobile-and-LG version of Android 2.3 running on the T-Mobile G2X. This phone also goes by the name of Optimus 2X. Soon enough I’ll ditch it for the Galaxy Nexus, but I digress.

This what I get trying to ping after turning back on the “Enable Data” option. Hooray. All it took was doing an action equivalently counterintuitive to the famous “Oh you want to shut down? First, go to start…” paradigm. At least this one is unintentional.

Also, you may be wondering why on earth I would be using USB Tethering when my phone provides WiFi Hotspot capability. The answer is that I’ve found the latency to be lower and the signal to be more consistent using USB Tethering. The bandwidth is also higher, which I suppose makes sense because one phone using the same wireless antenna for sending and receiving data probably isn’t sending as fast as it could be. I should be careful to specify that I don’t actually know that is what’s happening. Anyway, USB Tethering gives me a faster connection. The other perk is that my laptop, which I’m going to plug in when I get to work, can power the phone while it’s on USB Tethering. With WiFi Hotspotting, I’m pretty guaranteed to drain my phone to the point where I will have to plug it in to make it through the day. There you have it.

APKs Won’t Download In Android Browser

If you, like me, are attempting to setup a download end point for Android APK files that doesn’t include a .apk in the URL, you have probably butted your head against a myriad of issues. It’s worth noting that downloading APKs in Dolphin, Firefox Mobile, or Opera all work fine with respect to using Content-Disposition headers alone. It’s just the native browser that gives us grief. For the native browser, my first pass at the endpoint’s download code was the naive approach:

header('Content-Disposition: attachment; filename='.$apk_name);
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);

Testing this code in any desktop browser, I get the expected download notification and the file downloads like a champ. When I test drive this in the Android browser the result is different. Rather than getting a file download, I get the correct filename downloading but instead of using the necessary APK extension, the file is magically transformed into an HTM file, which is completely useless. This seems like a painfully bad decision for the content handling mechanisms of the browser, perhaps even a bug. Looking for some sign in the Android logs, I see this:

D/MediaScannerService( 1386): IMediaScannerService.scanFile: /mnt/sdcard/download/that_apk_file.htm mimeType: text/html

So while this is completely bad behavior, at least there’s a trail of hope in it — MIME type. Text/HTML is not the MIME type an APK file should have and apparently the browser is making us play by the rules. I can understand that. After all, if the MIME type is the source of truth then an APK file extension is only going to make the OS confused about what to do with the file. So, the question becomes what file type should an Android APK file have?

Doing some Googling about it, I find Wikipedia has the answer. The APK file format entry explains that Android APK files should have a MIME type of application/vnd.android.package-archive. Armed with this knowledge, I tried modifying my endpoint to send the MIME type I want:

header('Content-Disposition: attachment; filename='.$apk_name);
header('Content-Type: application/vnd.android.package-archive');
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);

Hitting the endpoint now, I find that the browser gets the APK file with the APK extension. Booyah. Just for rigor, I check logcat, and see what I was hoping to see:

D/MediaScannerService( 1386): IMediaScannerService.scanFile: /mnt/sdcard/download/that_apk_file.apk mimeType: application/vnd.android.package-archive

This is good, but I notice there are still flaws in the pearl. Namely, the file downloads without an indication of filesize or progress. The downloads page shows only that the download is “in progess” all the way up to completion. On a slow connection, this will seem like the file is stuck. Not ideal.

So, knowing a little about the HTTP spec, it seems like what the browser might need is a Content-Length header to get a handle on the progress towards completion. This is a straight-forward change, requiring just a single line.

header('Content-Disposition: attachment; filename='.$apk_name);
header('Content-Type: application/vnd.android.package-archive');
header('Content-Length: '.filesize($web_inaccessible_dir.'/'.$apk_name));
echo file_get_contents($web_inaccessible_dir.'/'.$apk_name);

Giving this a whirl in the browser, I find that I now have both the correct file extension and a progress bar during the download. Huzza.

I believe I’ve got what I want, but with Android it’s not guaranteed to be that simple. Due to fragmentation, I can only assume this works on the devices I’ve tested. So far, that includes the T-Mobile G2X (2.3.3 + T-mo’s bloatware), the x86 Android Emulator running 2.2 (by the way, if you haven’t tried out the x86 Emulator for Android and are still using the ARM emulator, you need to check out http://www.android-x86.org/ — it will change your whole perspective on shit), and the HTC Sensation 4G running 2.3.4 + Sense UI. I feel like that’s a good enough start to write this post. Please comment if you have an OS version/phone that doesn’t work with this setup.