Monday, June 18, 2012

Making Persistent Changes to an Android Emulator

When working on an Android emulator, we sometimes want to make changes that can survive a reboot.  This isn’t a problem for things like installing applications (which is usually what you are using the emulator for), because of the way that the AVD Manager creates your virtual device.  However, system changes (that is, changes to the /system partition) will not survive on a reboot.  There are legitimate reasons to do this; here are just a few off the top of my head:

  • Adding or modifying system binaries in the /system/bin or /system/xbin directories
  • Adding entries to the /system/etc/hosts file for redirection for specific domains
  • Changing settings in the /system/build.prop file such as the device manufacture and phone model (for apps that refuse to run on certain devices)
  • Adding entries to the android cert file at /system/etc/security/cacerts.bks so that the device trusts self-signed certificates (again, for proxying)
This post will show you how you can make changes to files on the system partition on your Android emulator that will persist.

First let’s make an Android virtual device (AVD).  I’m going to use API 8, the standard Froyo distribution, for the target.  Note that I’m creating a SD card image with this AVD that is 512MB.  This should be plenty of space to store temporary system image.  I chose the name “Froyo” but you can use whatever you like.



When we press “Create AVD”, the AVD Manager creates some files in the ~/.android/avd/ directory (this location may vary on Windows).  Specifically, it creates:
  • [avd_name].ini - A file containing configuration settings that point to the AVD’s directory
  • [avd_name].avd - A directory containing settings and file-system images specific to this AVD – the SD card image and the user data image are a couple examples (others are created after the AVD boots up)
Our ./Froyo.avd/config.ini file looks like this:

The contents of the Froyo.avd directory:

 
The remaining file-system images are common to all AVD of a specific API level, and can be found at $SDK_HOME/platforms/android-*/images/.  This will focus on changes to the system.img partition.  This, by default, is mounted as read-only (just as a real device would), and even if you remount the drive and make changes, they will not persist on a system reboot.  Our goal is to make a custom system.img file, and have the AVD load this image instead of the default image.

Before we get started, you’ll need to obtain a copy of the mkfs.yaffs2 utility compiled for ARM.  Go ahead and start our new emulator, making sure to specify a safe “partition-size”.

 

Once the emulator boots, we can make the changes we wish to reflect on our custom system image.  For this example, I’ll change the “ro.product.brand” field in the system/build.prop file, but you could do any changes you want.  First we need to pull the /system/build.prop file so that we can edit it (no editor is available from the shell).  We can accomplish this with the command:

 
 
Then we can edit the file (Disclaimer - editing this file may affect the stability of the system!):

 

Now we can remount the system partition as read-write and push the new file to the device:

 

If all your changes are complete, the next step is to run the mkfs.yaffs2 utility on the device to create a *.img file we can use.  To make this easier, I wrote a small shell script (getimage.sh) to automate the steps.  The steps should be easily followed by reading the script.




Running the script yields the following output:

  

We now have a file called _system.img in our current working directory.  At this point we have two options.

Option 1 – Replace the Global system.img
If you are alright with the change applying to all of the AVD’s that you run at this particular API level, you can simply move the new image where the old image was.  It’s important to back-up the old system.img in case something went terribly wrong.  As stated above, the image files are located at $SDK_HOME/platforms/android-*/images/.



Now when you restart your AVD, you should see the changes.

Option 2 – Modify the AVD's Configuration
Let’s say that you want this change to apply only to this specific AVD.  This can be accomplished by cloning the images directory, and then modifying our AVD's configuration.



The line we need to change is the "image.sysdir.1" parameter of the Froyo.avd/config.ini file:



And we're done!  Hope this helps some people with customizing their emulator images.

-jakev

8 comments:

  1. This method does retain changes in the system partition, but it requires us to do this each and every time we decide to make a new change. I wish there was a better way.

    ReplyDelete
  2. Thanks very much! Very appreciated your post. Solved my life. I was trying to persist changes on /etc/hosts file, followed instructions and now I can

    Thanks!

    ReplyDelete
    Replies
    1. Thanks! Glad to hear it was useful. Modifying the /etc/hosts file is one of my main uses of this technique as well!

      Delete
  3. Hi! I've followed your guide but I dont get it work with Android 4.0+. It seems that the image created with mkyaffs2 doesnt work with an emulator running device with that version. Have there been any changes in the system.img format?

    ReplyDelete
    Replies
    1. Jaime, I will perform a few tests and let you know. Are you using the ARM, Intel x86, or MIPS system image? I think I know what the issue is. :)

      Jake

      Delete
    2. ARM,
      In the meantime I've found a workaround:
      http://stackoverflow.com/a/15419162/2478547

      But it seems that build.prop has a special behavior because when I push a modified version the emulator doesn't boot. I've realized that the reason was the file permissions. If build.prop has 666 (the default when push a file) the emulator doesn't boot, it must has 600 or 644.

      Delete
  4. I need a copy of the mkfs.yaffs2 utility compiled for Intel-x86.
    How can I get it ?

    ReplyDelete
  5. Can you please write a review on this texting spy app?

    ReplyDelete