MonthNovember 2011

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.

Here We Go Again

This is the first post of the newest iteration of my corner of the web. Enjoy and converse. In case you’re curious, this is a WordPress blog. I’m not a WordPress fanboy, but it is the best tool for the job as far as I’m concerned. I rebuilt most of the blog twice before switching to WP. The first time, I wrote a directory parser/cacher system that would let me statically generate the blog from flat files. The second time, I wrote an even more sophisticated version that was still file-backed rather than database backed. I was doing a lot of fancy caching, minimizing, and dog fooding. Still, I wasn’t getting it done fast enough. Who cares who fast a blog loads if it isn’t done enough to launch. Exactly no one, including me. After spending probably fifteen hours on each I had the epiphany that just using WordPress will deliver significantly more bang for the time I spend. At the time of this writitng I have approximately 7 hours in the project and it’s nearly complete in terms of desired layout and features for desktops and tablets. The handheld mobile view sucks, but I haven’t spent any time optimizing for that yet. I’ll get there. Anyway, now that this bad boy is up and running, look for frequent updates with interesting things.