Tuesday, April 19, 2011

How to Patch A Stock Android Application

Introduction

When developing an Android application that depended on the stock Android application Sound Recorder, I found a bug in the latter application. This post describes how one would patch such a stock Android application and submit the fix. The bug and the fix are not as relevant as the process of working with the Android Open Source Project (AOSP) code base. Gaps in the source documentation are filled in here.

Note that this post is tailored to development in Ubuntu 10.10 as that was my environment.

Prerequisites

Before fetching and building the AOSP, one should realize or do a few things:
  • Search the Android issue tracker and mailing lists to figure out if anyone has encountered this before and if a fix is in progress.
  • Browse the source to try to diagnose the issue before doing anything. Hooray for open source! Each application is in a separate git tree.
  • Once the issue is diagnosed and if it's not already an existing issue, post to the Android issue tracker. This allows others to see the status of the bug, make comments, and get others involved in a fix. If you can't fix it, at least someone else will be aware of the issue and could take on the effort.
  • It seems like the entire AOSP has to be built in order to build the single stock application that one wishes to patch. I could not find a source that described more surgical building without building everything else. Stock applications use private APIs that are not published in the SDK so those dependencies have to be built. It's probably just easiest to build everything to get to the task at hand.

Initializing the Build Environment

The official steps at the time of the writing work with the following exceptions:
  • OpenJDK 6 works. You do not have to get the Sun JDK. To check your installation, use the command "java -version" in a terminal.
  • The required packages list is wrong; apt-get will complain that it cannot locate packages. See the first answer on this Stack Exchange post.

Downloading the Source

Nothing wrong actually happened here using the official steps! Download time varies depending on your interwebs connection; it took several minutes for me. My working directory was $HOME/aosp; all following instructions will assume this is your current directory.

Building and Running

The official steps did not indicate how much pain would be involved. A bunch of issues were encountered.

Processor Architecture 32-bit vs. 64-bit

Based on mailing list discussions, building is only supported for 64-bit machines for releases after Froyo (v2.2), but the community has found ways to patch the source to allow 32-bit builds. When trying to build on a 32-bit machine, you'll get a warning from the make command. A few makefiles need to be editing for building to proceed. Do as follows:
  • In build/core/main.mk, comment out the check for 64-bit versions (search for 64 to find the if-statement).
  • Search for makefiles with explicit 64-bit build flags and comment out variable assignments that add those flags:
    find . -name '*.mk' | xargs grep m64
For example, here is the changes I made to one of the makefiles with explicit flags:
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index d3ab3dd..be99ad4 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -249,9 +249,9 @@ LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
 endif
 
 ifdef CONFIG_DRIVER_PS3
-L_CFLAGS += -DCONFIG_DRIVER_PS3 -m64
+L_CFLAGS += -DCONFIG_DRIVER_PS3 # -m64
 OBJS_d += src/drivers/driver_ps3.c
-LDFLAGS += -m64
+#LDFLAGS += -m64
 endif
 
 ifdef CONFIG_DRIVER_IPHONE

Once the build starts, it took at least an hour on my machine (Core 2 Duo, 2.0 Ghz). It's building almost everything you can think of except the kernel. The built binaries will be in the out directory.

Emulator Fails to Run

Before applying our fix, let's at least make sure that our environment works. Run the emulator at out/host/linux-<ARCH>/bin/emulator.
emulator: ERROR: You did not specify a virtual device name, and the system directory could not be found.

If you are an Android SDK user, please use '@' or '-avd ' to start a given virtual device (see -help-avd for details).

Otherwise, follow the instructions in -help-disk-images to start the emulator

By default, the emulator looks for default system images (*.img), containing the root file system among other things, in $ANDROID_PRODUCT_OUT. This shell variable is defined only if you ran the lunch program in the same shell as the emulator. You can rerun lunch in any shell to define these variables.

Do not use a prebuilt SDK to test applications built from the master branch of AOSP. They are built with a special SDK version that is newer than any prebuilt SDK version; therefore, the patched application will fail to install (INSTALL_FAILED_OLDER_SDK). Using the Android Asset Packaging Tool (aapt), we can see this special version of the applications built:

$ aapt dump badging $ANDROID_PRODUCT_OUT/system/app/SoundRecorder.apk 
package: name='com.android.soundrecorder' versionCode='10' versionName='AOSP'
sdkVersion:'AOSP'
targetSdkVersion:'AOSP'

No SD Card

If testing requires a SD card, create one that the emulator can use as follows:
out/host/linux-<ARCH>/bin/mksdcard SIZE $ANDROID_PRODUCT_OUT/sdcard.img
See the help documentation for how to express size (mksdcard -help). For example, my SIZE was 32M to produce a 32 MiB file. The second argument is the default location of the emulator's SD card image file.

Using a Test Application

I used my Android application to exercise to demonstrate the bug in the stock application. To install your application, use the Android Debug Bridge (adb):
$ out/host/linux-<ARCH>/bin/adb install LOCATION/OF/TEST/APP

Applying Your Fix

Assuming that the emulator is operational and you've demonstrated the bug, apply the fix and rebuild. The shell function mm is defined in build/env.sh and will rebuild only the module in the current directory.
$ cd packages/apps/THE_APP_TO_FIX
$ (edit files as necessary)
$ mm 

Restart the emulator to pick up the changes. Even though the Android Debug Bridge (adb) provides a sync capability to push the changed file to the emulator, you might get an "out of memory" error. Let's keep it simple for now.


Submitting the Fix

After the fix has been verified and committed locally, it's time to submit it to the world! Before you can use the repo tool to upload the fix, set up your account on r.android.com:

  • Submit your SSH public key as uploading uses the SSH protocol.
  • Sign a contributor agreement. Individuals can do this completely online.

The successful upload will be viewable at the source review site. To improve the chance of getting accepted, add a reviewer who was involved in the development of the application. The commit log should indicate who would be most appropriate as a reviewer.

Closing

For such a simple patch, I ended up doing a lot of work, but it was worth it in the end. I've been a user of open source for a while but have never contributed back until recently. Even though it is a small contribution, it's a start. A lot of people making small contributions add to something big. Also, it's awesome to think that my small change could be distributed to millions of devices.

Saturday, April 2, 2011

Simple, Effective Password Management with Keepass and Dropbox

Disclaimer:  Even though I used KeePass for 2 years and donated to the project, I have migrated to LastPass as I wanted a product with support for the long-term. It's also more convenient with browser integration (auto-filling of forms instead of copy-pasting), it doesn't have a .NET dependency, and I don't have to rely on a separate party like DropBox for backups.

Have you ever counted how many online accounts you have?  Online accounts are used for almost everything in our lives:
  • Social networking
  • E-mail
  • Finance & credit
  • Utilities
  • Shopping
  • Insurance
And more.  How do you manage all of the passwords?  With so many accounts, it's easy to be lazy by writing passwords in plain text file, reusing passwords across sites, using very weak passwords, or even depending exclusively on security questions to get into an account because it's used rarely (e.g., biannual payments to a service).  After examining my accounts, I even found that the password to one account was a username of another.  I also found out that I had over 20 online accounts.

Perhaps you think it's not a big deal.  Perhaps.  But have you ever read about how a bunch of internal documents from Twitter were released 2 years ago?  It's intriguing.  The hacker didn't use complex technical exploits at all; he simply broke into one weak account and used it to work up the chain of more valuable accounts.

Luckily, there are mature tools out there to help manage password-authenticated accounts.  I use KeePass, an easy-to-use password manager that achieves the following:
  • Confidentiality: all stored passwords and account information are stored in an encrypted file.
  • Strength: the program can generate long and complex passwords for you (less vulnerable to someone guessing or cracking the password).
KeePass is also multi-platform as it can run on Windows as well as Linux (I've actually tested this).  In short, you remember one master password that is used to derive a key to encrypt a password database file.  Provide the master password to access and modify the password database.  When you need to use a password, use the program to copy the password from the database and then paste it into the web browser.  When coming up with a master password, consider using a long but simple password like a sentence or a quote.  You must be able to remember it but prevent others from simply guessing it.  My master password is over 30 characters long.

When converting passwords from old to new KeePass-generated ones, consider the following:
  • After changing your password, test it!  You might have accidentally pasted the wrong password, or the site did not accept the password.  Some sites will not accept the generated password generated using KeePass default settings.  For example, some sites have maximum password lengths like 8 characters (WTF?), but KeePass generates 20-character passwords by default.  Even worse, you might not realize this until you change the password, log out, and then fail to log in.  If you copy a password into a field with a maximum length restriction, the password will be truncated to the maximum length without notice.  Do a quick check on the password rules for the site when changing the account password.  Then modify the password generation rules to shorten the password as appropriate for that specific password (stick with defaults otherwise). 
  • One site actually prevented pasting of the password using Javascript.  While it might be instinctive to disable Javascript in the browser and paste anyway, this can cause more trouble.  I just displayed the password in KeePass and manually typed each character.  Lame.
  • For some reason that I didn't look into deeply, I couldn't paste my password into a terminal prompt in GNOME on Ubuntu when creating a SSH key pair with passphrase.  I had to use the key creation tools in Ubuntu 10.10:  System >> Passwords and Encryption Keys >> My Personal Keys.    
Great, say you've changed your passwords on many of the accounts to stronger KeePass-generated passwords.  But now there is a new problem:  backup the password file or risk losing the passwords to every online account.  I use Dropbox to backup my password file.  The free plan has plenty of space and can be accessed and updated from multiple machines cross-platform.  

Now what if you want to secure the Dropbox account with a strong KeePass-generated password?  Make the password database a public file in Dropbox.  Then you can access it from anywhere without barriers and still retain password confidentiality.  I also keep a copy of the KeePass binary zip as a public file so that I can easily set up any machine to access my passwords.  Grab the password file, the binary zip, and go!  The only catch is that you have to remember the master password (obviously) and the URLs to the public Dropbox files.  The URL is in the following format:

http://dl.dropbox.com/u/USER_ID_NUMBER/YOUR_FILE_NAME.  

One can figure out a good way to remember the URLs:  note it down in your phone, use a copy of the password database to get to the latest password database, etc.

But what about mobile?  KeePassDroid works too for Android smartphones.  You can put a copy of the password database on the smartphone (for simplicity if rarely authenticating as I do) or use Dropbox on your Android smartphone to always use the latest password database (haven't tested this).     

Using this setup for several months now, it has been quite easy and definitely more secure than my previous password mismanagement.  Comments on improving the setup are welcome!