Android::Introduction()

The BeagleBone Black hardware platform is well suited to acting as a prototying platform and learning tool for exploring the Android OS. The Rowboat project currently has support for building Android for the BBB, but there is a problem: Rowboat builds Android for a 3.2 Linux kernel, and the BBB uses a 3.8 Linux kernel. This means that the following limitations are present in the Rowboat build:

  • There is no Device Tree support, which allows BBB users to configure the multiplexing of pins for GPIO and Capebus boards.
  • Audio and video output must go through external cape boards, rather than the built in HDMI "cape" that is built into the BBB itself.
  • Any patches to the 3.8 Linux kernel tree must be retrofitted for the 3.2 kernel, which is a lot of work.

After a lot of research and tinkering, I've managed to build Android with a 3.8 Linux kernel. This page will give an overview of the process and (hopefully) help you in creating your own Android builds for the BBB.

Android::Overview()

Before we get started, you might like to try a pre-built image of Android for the BBB that I have already made. You can download it here (about 700 MB in size), decompress it with bunzip2, and dd it onto a microSD card of at least 4 GB in size. Put the microSD card into your BBB, power it on, and Android will boot and come up. Use a USB mouse (and a USB keyboard, too, if you'd like) connected to the BBB's host USB port to interact with the system. Audio and video will be sent over the HDMI connection.

The CircuitCo wiki has step-by-step instructions for installing this pre-built image. The first boot of your Android system using the image will take about three minutes, since there is some "first time" system setup that takes place. On subsequent boots, it will only take about a minute before you are up and running.


Android 4.2.2 (Jellybean) running on the BBB with a 3.8.13 kernel

Building Android will involve the following steps:

  • Setup the build environment for your system for building Android.
  • Fetch the Android source.
  • Fetch and build the 3.8 Linux kernel, along with the necessary patches.
  • Fetch and build the U-Boot bootloader source.
  • Build the Android rootfs.
  • Copy everything to a microSD card.
  • Configure your Android system properly for the BBB.

Android::Setup()

Important Note: You must have a 64-bit build environment to build Android. You must have at least 35 GB of free space to do a complete build, and it takes approximately 5 to 6 hours to download and build everything (assuming that you have a fast network connection).

When you build Android, you must have the proper tools and libraries installed on your system. This can be a bit tricky, but luckily the Rowboat team has already documented how to do this. Follow their instructions to get all of the necessary packages installed. Unfortunately, this process is a bit too involved for me to personally help you troubleshoot. If you run into problems, please refer to the Rowboat team for assistance. This part of the process is always the same, no matter what kernel you end up building for Android, so any existing troubleshooting documentation from the Rowboat team for getting through this step will apply to you.

Note: Since so many people have mentioned that they can't find the correct version of Java, I wanted to provide a link to the Java JDK that you'll need. Download the "Linux x64" file (jdk-6u45-linux-x64.bin). You'll have to register with Oracle to download it.

Once you've managed to get your build environment all set up, it is time to begin fetching the source code that you'll need. We'll assume that you are using your home directory (/home/user) to build Android, but if you have some other location where you are building, just change the paths in the commands accordingly.

Android::Fetch("Android")

You can fetch the Android sources for BBB by executing the following shell commands:

$ mkdir rowboat-android
$ cd rowboat-android
$ repo init -u git://gitorious.org/rowboat/manifest.git -m rowboat-jb-am335x.xml
$ repo sync

If the repo sync gives you trouble (it crashes or hangs), see if this helps you to troubleshoot your problem. Personally, I had a problem with the repo sync hanging at 99%, and it turned out that (on my Ubuntu 12.04 VM) I needed to upgrade my git to a newer version. This fetch will take quite a while to complete, so be patient.

Android::Fetch_Build("Kernel")

An Android kernel is a Linux kernel with some additional Android features built into it. These extra Android features became part of the Linux kernel mainline in version 3.3. This means that if you have a 3.8 kernel that runs well on the BBB, you'll have an Android kernel by just enabling these extra features in that kernel. The extra items (like Binder IPC and out-of-memory management) can be safely built into a Linux kernel (increasing the size of the kernel by about 25k for a compressed zImage) and run on a Linux system, so there is no harm in using the same kernel for both an Android system and a Linux system.

We'll be using Robert Nelson's 3.8.x kernel tree as the kernel for our Android system. Follow the steps within the "Linux Kernel" section of the BeagleBone Black portion of Robert's wiki to fetch the kernel source and build scripts. When you run the build script, a variety of patches will be applied to the kernel source tree. But, more importantly, a full x86-to-ARM cross-compiler toolchain will be downloaded. This is the toolchain that we'll use to build the kernel, bootloader, and Android.

Eventually, the build script will launch the kernel configuration utility (kConfig). Scroll down to "Device Drivers" and select it:

On the new menu, scroll down to "Staging drivers", enable it (by pressing the spacebar) if it is not enabled by default. An asterisk will appear in the brackets when staging drivers are enabled:

Once staging drivers are enabled, select it to enter the "Staging drivers" menu. Scroll down to "Android" and select it:

Enable the "Android Drivers" option, and then enable all of the new options that appears underneath it. For "Android Log Driver" and "Android timed gpio driver", you have the option of building them as modules. I just build them directly into the kernel:

Once this is done, select the "Exit" option 4 times to travel back up the menu heirarchy and exit. You will see the following screen:

Select "Yes", and the kernel will begin building.

Android::Fetch_Build("Bootloader")

The bootloader that is used for our Android system is U-Boot. By default, Rowboat uses a v2013.01 U-Boot. This version is too old, as it doesn't have the device tree support that we need. You'll need v2013.04, instead. Robert Nelson has also patched v2013.04 to add several fixes. Use the following commands to download U-Boot and patch it:

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot/
$ git checkout v2013.04 -b tmp
$ wget https://raw.github.com/eewiki/u-boot-patches/master/v2013.04/0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch
$ patch -p1 < 0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch

Assuming that you placed the kernel "linux-dev" directory in your home directory, and that your home directory is "/home/user", build U-Boot with the following commands:

$ cd u-boot/
$ make ARCH=arm CROSS_COMPILE=/home/user/linux-dev/dl/gcc-linaro-arm-linux-gnueabihf-4.7-2013.04-20130415_linux/bin/arm-linux-gnueabihf- distclean
$ make ARCH=arm CROSS_COMPILE=/home/user/linux-dev/dl/gcc-linaro-arm-linux-gnueabihf-4.7-2013.04-20130415_linux/bin/arm-linux-gnueabihf- am335x_evm_config
$ make ARCH=arm CROSS_COMPILE=/home/user/linux-dev/dl/gcc-linaro-arm-linux-gnueabihf-4.7-2013.04-20130415_linux/bin/arm-linux-gnueabihf-

You can also use environmental variables to specify the compiler, or make a small shell script that runs these commands for you. Whatever is the most convenient for you. The important part is that you are telling the bootloader to build using the cross-compilation toolchain that is already provided with the kernel.

Android::Build("Filesystem")

Now that your kernel is configured and built, you are ready to build the Android filesystem using the Rowboat sources that you fetched. But, you don't want the Android system libraries to reference the 3.2 kernel that is included in Rowboat. To point Rowboat to the correct kernel, use the following commands:

$ cd rowboat-android
$ mv kernel kernel.backup
$ ln -s /home/user/linux-dev/KERNEL kernel

The makefile in Rowboat assumes that the prefix of the cross-compiler that it is using is "arm-eabi-", but the cross-compiler that you used to build the kernel does not have this prefix. To address this, you'll need to modify the Rowboat makefile. First, make a backup of the existing makefile. The makefile is read-only, so change its permissions so that you can modify it:

$ chmod 644 Makefile

Open up the makefile in a text editor and locate the following line at the top:

export PATH :=$(PATH):$(ANDROID_INSTALL_DIR)/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin

Comment out that line (by placing a '#' at the start of the line) and replace it with the following:

export PATH :=/home/user/linux-dev/dl/gcc-linaro-arm-linux-gnueabihf-4.7-2013.04-20130415_linux/bin:$(PATH)
export CC_PREFIX := arm-linux-gnueabihf-

The Rowboat makefile, unfortunately, hardcodes the compiler prefix of "arm-eabi-" in several places. You'll need to replace each of these references with "$(CC_PREFIX)". For example, one replacement that you need to make originally looks like this:

	$(MAKE) -C hardware/ti/wlan/mac80211/compat_wl18xx ANDROID_ROOT_DIR=$(ANDROID_INSTALL_DIR) CROSS_COMPILE=arm-eabi- ARCH=arm install

After replacement, it will look like this:

	$(MAKE) -C hardware/ti/wlan/mac80211/compat_wl18xx ANDROID_ROOT_DIR=$(ANDROID_INSTALL_DIR) CROSS_COMPILE=$(CC_PREFIX) ARCH=arm install

Once the changes have been made to the makefile, it is time to build Android. Execute the following command:

$ make TARGET_PRODUCT=beagleboneblack OMAPES=4.x droid

Feel free to add in "-j4" or "-j8" to make it build faster if you have the cores and RAM to support a parallel build. I like to go at least "-j2" because much of the compile process is I/O bound and two build processes can run on a single core without much CPU contention. The build process can take hours, so the more of the build you can run in parallel, the better.

Note: If the build fails right away with the following message (or something similar):

Checking build tools versions...
************************************************************
You are attempting to build with the incorrect version
of java.
 
Your version is: java version "1.6.0_45".
The correct version is: Java SE 1.6.
 
Please follow the machine setup instructions at
    https://source.android.com/source/download.html
************************************************************
build/core/main.mk:141: *** stop.  Stop.

... then you've actually installed Java correctly. The build script is being too picky about the format of the version number string that Java reports. Comment out line 141 (the $(error stop) line) in the file rowboat-android/build/core/main.mk by placing a '#' at the start of the line. This will skip this error. Only do this if the script is reporting that your Java version is 1.6.*. If the reported version is something else, then you probably installed the wrong Java version and need to install the correct one before continuing. Once you've changed the main.mk file, run your "make" command again to resume building.

After the build has completed, you need to build the root filesystem of Android. Execute the following steps:

$ cd out/target/product/beagleboneblack
$ mkdir android_rootfs
$ cp -r root/* android_rootfs
$ cp -r system android_rootfs
$ cd android_rootfs/system
$ tar -xvzf /home/user/linux-dev/deploy/*modules.tar.gz

Prior to creating the final filesystem image, we need to change a few things. First, we need to set the proper init.rc for our BBB. Assuming that you are still in the out/target/product/beagleboneblack directory, backup the file android_rootfs/init.rc and then open it in a text editor. At the top, you'll find this line:

import /init.${ro.hardware}.rc

The ${ro.hardware} will resolve to "genericam335x", and no file named init.genericam335x.rc exists. Change that line to the following:

import /init.am335xevm.rc

Rowboat does not correctly set up the mount point for the usrdata filesystem, so you will have to change it. Backup the file android_rootfs/fstab.am335xevm and then open it in a text editor. You'll find a line that begins with this:

/dev/block/platform/omap/omap_hsmmc.0/mmcblk0p3

Change that to the following (while leaving the rest of that line alone):

/dev/block/mmcblk0p3

You'll need to disable hardware graphics acceleration, since SGX is not supported for the 3.8 kernel. Open the file android_rootfs/system/build.prop in a text editor. At the bottom of the file, add the following two lines:

debug.egl.hw=0
video.accelerate.hw=0

Finally, while in the out/target/product/beagleboneblack directory, execute the following command:

$ sudo ../../../../build/tools/mktarball.sh ../../../host/linux-x86/bin/fs_get_stats android_rootfs . rootfs rootfs.tar.bz2

You have now built the Android filesystem for the BeagleBone Black.

Android::Install()

Now that all of the pieces have been built, it is time to pull them all together and make a microSD card image. The Rowboat wiki will tell you to download RowboatTools-JB to get the "mkmmc-android.sh" script that you need to do this, but that script won't work for you. It isn't up-to-date, and won't set up the partitions and mount points properly. But, a newer version of this script exists in the Android source tree that you already have in the external/ti_android_utilities/am335x/mk-mmc directory. From your home directory, execute the following commands:

$ mkdir image
$ cp rowboat-android/external/ti_android_utilities/am335x/mk-mmc/* image
$ cp rowboat-android/out/target/product/beagleboneblack/rootfs.tar.bz2 image
$ cp u-boot/MLO image
$ cp u-boot/u-boot.img image
$ cp linux-dev/KERNEL/arch/arm/boot/zImage image
$ cp linux-dev/KERNEL/arch/arm/boot/dts/am335x-boneblack.dtb image

Create the file image/uEnv.txt in a text editor and place the following text in it (or just download this one and copy it into the "image" directory):

kernel_file=zImage
console=ttyO0,115200n8
mmcroot=/dev/mmcblk0p2 rw
mmcrootfstype=ext4 rootwait
loadkernel=load mmc ${mmcdev}:${mmcpart} 0x80200000 ${kernel_file}
loadfdt=load mmc ${mmcdev}:${mmcpart} 0x815f0000 ${fdtfile}
boot_ftd=run loadkernel; run loadfdt
mmcargs=setenv bootargs consoleblank=0 console=${console} androidboot.console=ttyO0 mem=512M root=${mmcroot} rootfstype=${mmcrootfstype} init=/init ip=off video=720x480-16@60 qemu=1 vt.global_cursor_default=0
uenvcmd=run boot_ftd; run mmcargs; bootz 0x80200000 - 0x815f0000

Before you continue, determine what device on your system is your microSD card. On my system, it is /dev/sdb (/dev/sda is the hard drive). But, it may be different on yours. Make sure that you know for certain that you have identified the proper device, since that device will be wiped clean by the next commands:

$ cd image
$ sudo ./mkmmc-android.sh [YOUR DEVICE FILE FOR THE MICROSD CARD] MLO u-boot.img zImage uEnv.txt rootfs.tar.bz2

Once that is done, mount the /boot partition of the microSD card on your system and execute the following commands:

$ cd /media/boot [OR WHATEVER PLACE THE BOOT PARTITION IS MOUNTED]
$ sudo mv uImage zImage
$ sudo cp /home/user/image/am335x-boneblack.dtb .
$ sync

The reason for the rename of "uImage" to "zImage" is because the mkmmc-android.sh script always copies the kernel image to the filename "uImage" on the boot partition. You might not want to have it stay that name, however, since it can be misleading. Once the microSD card image is set up, be sure to rename the uImage file to something more appropriate, if necessary. At the very least, make sure your uImage is renamed to the kernel file that is specified in the uEnv.txt!

Umount the microSD card, insert into your BBB, power up the system, and you now have Android running on the BBB.

Android::Configure()

Now that you have a working Android system, you might want to play around with its configuration. Most people will just be interested in changing the resolution, which can be accomplished by changing the "video" kernel command line parameter in uEnv.txt. By default, I have set this to "720x480-16@60", which is 720x480, 16 bits per pixel color depth, and 60Hz refresh rate.

Important Note: If you remove the "video" setting completely, your resolution will default to the highest resolution that your display reports via EDID. If your display can't report any EDID information, the resolution will default to 1024x768. If your system is running, but you don't get any video, try removing the "video" setting from your uEnv.txt.

I have personally verified that the CircuitCo LCD3, LCD4, and LCD7 BeagleBone Capes all work correctly (both video and touchscreen) with this Android image. The only change that must be made is that the "video" option in uEnv.txt must be removed so that the default resolution is set. This default resolution is determined by the firmware, so don't try to force a resolution by explicitly setting it. These displays have been verified as properly loading their firmware files dynamically from within the Android root file system. 4D Systems has contacted me to let me know that they have tested their 4DCAPE-43T LCD cape with this image and that it is working properly. No other capes have been tested that I am aware of.

Because the BBB's HDMI output is being used for output, you have two choices for explicitly setting resolutions in uEnv.txt. You can either set a CEA mode, which is a video mode that supports sending audio data, or a non-CEA mode, which is video only (like a DVI signal). If you are using a DVI/VGA computer monitor for your video, then you can use a non-CEA video mode for your display. If you are using an HDMI television, then you can use a CEA mode. The bad news in this situation is that, while some video modes are both CEA and non-CEA, there are many that are only on one list or the other, but not both.

For example, 640x480 is a non-CEA mode. Using "video=640x480-16@60" will not give you any audio. 720x480 is a CEA mode. Using "video=720x480-16@60" will give you audio, but it probably isn't a valid mode for many VGA monitors. To view the resolutions for valid CEA modes (modes that support audio over the HDMI) look at the edid_cea_modes[] array in linux-dev/KERNEL/drivers/gpu/drm/drm_edid_modes.h. Lists of other valid modes are in the same file in the drm_dmt_modes[] and edid_est_modes[] arrays.

Adding other kernel command-line options (mpurate, debugfs, etc.) is done in the uEnv.txt in the same fashion. Just add the additional options on the end of the line that the "video" setting is on.

The "qemu=1" on the kernel command line is used to disable certain aspects of hardware graphics acceleration. Once SGX support is ready for the 3.8 kernel, you'll be able to build hardware-accelerated OpenGL for BBB's Android and remove that qemu option. For now, though, it has to be in there.



(I'm a PhD student, doing research on virtual machines, embedded systems, and security. Donate to help me purchase more hardware and software tools to develop other awesome open source embedded stuff!)