Updated: Oct 2013 – Non-root users can now use the network, and as a result the VNC server doesn’t need to be run as root anymore.
Updated: Feb 2013 – Made a bunch of corrections and added a few steps
Updated: May 2013 – I bought a Nexus 10, and I’ve moved my Lubuntu chroot over to the Nexus 10. No issues, I’d expect the same steps to work.
This tutorial documents the steps I went through to get a working Ubuntu system (Lubuntu) on my Android device, an Asus Padfone. I decided to use the ARM hard-float port (armhf) instead of armle in order to try to extract better performance from the processor.
The tutorial was written based on the notes I made during the process. This tutorial is not for the faint of heart, and it’s best if you have some familiarity with Linux and aren’t afraid of the terminal. I’m not sure if I missed our anything in my notes, so let me know in the comments if anything doesn’t work.
In this first post, I’ll only cover getting the command line chroot working. In the next post, I’ll cover getting the GUI working.
Update: The steps for getting a GUI are now given in part 3, below.
Also, I believe there’s an app that does exactly what I’m trying to do here, but I don’t know if it supports the Padfone yet. If you’re not so comfortable with the terminal, this is probably the way to go.
The following were webpages that I found useful. Some even have almost all the steps you need to do this.
- A rooted padfone
- Terminal Emulator – I like this app because the hardware keyboard on the Padfone dock functions like a PC keyboard, with the back button functioning as escape. Very useful, especially when using Vim.
- At least 2GB of space on your SD card or internal storage. (Following this tutorial exactly will use 3.5GB)
- An Ubuntu/Lubuntu environment (Might work even on a live CD)
I also have busybox on my Padfone, but I’m not sure if it’s necessary.
Part 1 – Creating the bootstrap image
We are going to create a filesystem image to contain the entire Lubuntu installation. Then we will copy it to the Padfone.
On your Linux desktop (or server!) ensure you have debootstrap installed:
sudo apt-get install debootstrap
Change directory to a partition with enough space. (3.5G in this tutorial, 2GB minimum) For example:
Create the empty disk image file:
dd if=/dev/zero of=img.lubuntu-armhf.root bs=8k count=458752 sudo mkfs.ext4 -L chroot -c img.lubuntu-armhf.root sudo tune2fs -c 0 img.lubuntu-armhf.root
This creates a 3.5GB filesystem. If you want something smaller, like 2GB, substitute 458752 with 262144. Bear in mind that if you make your filesystem only 2GB, you’ll only have about 200MB free after installing the Lubuntu GUI.
Now, loop mount the newly created filesystem:
mkdir chroot sudo mount -o loop img.lubuntu-armhf.root chroot
Now begin downloading the core system files.
sudo debootstrap --arch armhf --foreign quantal chroot http://ports.ubuntu.com/
Take a break while it downloads the core files. Once that is done, unmount the filesystem:
cd ../../ sudo umount chroot
Then copy img.lubuntu-armhf.root to your phone.
Part 2 – Setting up the system
Now that we have the bootstrapped filesystem on the phone, we’re going to set it up and install some software packages.
First, open Terminal Emulator, become root, and navigate to the where the image is. In my case, I copied the image into my SD card, which is mounted at /Removable/MicroSD on the phone. If you copied your image elsewhere, you should change directory there instead.
su cd /Removable/MicroSD
Now, mount the filesystem, setup a minimal environment, and chroot into it. You will need busybox in order to chroot. In my phone, busybox is installed in /system/bin-busybox.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/system/bin-busybox" export TERM=linux export HOME=/root export USER=root export UID=0 mount -o loop img.lubuntu-armhf.root lubuntu mount -t proc proc lubuntu/proc mount -t sysfs sysfs lubuntu/sys mkdir lubuntu/dev/pts mount -t devpts devpts lubuntu/dev/pts chroot lubuntu /bin/bash
If the mount or chroot fails, it’s possible then that busybox is actually required to work. In that case, install busybox and try those commands again.
Now that we’re in, let’s finish up the installation:
Symlink initctl to true so that the fact that upstart doesn’t work does not give us lots of warnings.
dpkg-divert --local --rename --add /sbin/initctl cp /bin/true /sbin/initctl
Now do some additional basic configuration. (These settings are for my locale, which is English, Singapore. Modify the commands as necessary to match your actual locale.)
locale-gen en_SG en_SG.UTF-8 dpkg-reconfigure tzdata
Set the hostname and DNS server:
echo localhost > /etc/hostname echo 'nameserver 126.96.36.199' > /etc/resolv.conf
Next, open /etc/apt/sources.list in an editor and replace the contents with this:
deb http://ports.ubuntu.com/ quantal main universe multiverse restricted deb http://ports.ubuntu.com/ quantal-updates main universe multiverse restricted deb http://ports.ubuntu.com/ quantal-security main universe multiverse restricted
Ok, let’s get the rest of the software. These are all optional, of course, but I like to have SSH, vim and tmux.
apt-get update apt-get install ubuntu-standard vim tmux openssh-client openssh-server apt-get clean rm /var/run/reboot-required*
Ok, now let’s create a non-root user and disable the root password. Of course, replace jack with a user name of your choosing.
passwd -l root adduser jack usermod -a -G sudo jack
On Android, users which aren’t part of the AID_INET group (Group ID 3003) will not be able to use IP sockets at all. To get around this problem, we’ll create a group called aid_inet in the chroot with the correct group ID and make the non-root user (“jack”) a member of this group.
groupadd --gid 3003 aid_inet usermod -a -G aid_inet jack
One last thing, it’s convenient to have a script to be able to get into the chroot quickly. Create a file called enter_chrootand place the following script in it:
#!/system/bin/sh PREFIX=/Removable/MicroSD ROOTFS=$PREFIX/lubuntu if [ ! -d $ROOTFS/etc ]; then echo "Mounting lubuntu chroot..." mount -o loop $PREFIX/img.lubuntu-armhf.root $ROOTFS mount -t proc proc $ROOTFS/proc mount -t sysfs sysfs $ROOTFS/sys mount -t devpts devpts $ROOTFS/dev/pts mount --bind /sdcard $ROOTFS/mnt/sdcard mount --bind $PREFIX $ROOTFS/mnt/MicroSD fi echo "Setting environment vars" export TERM=linux export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" export USER=jack export SHELL=/bin/bash export HOME=/root export LANG=C echo "Starting shell as user $USER" /system/bin/chroot $ROOTFS sudo -u $USER -i
This script mounts the filesystems (if they haven’t been mounted) sets up the environment, then enters the chroot properly. Now, to enter the chroot:
The “sh” is necessary because files are not allowed to be executable on the sdcard.
Ok, the command line environment is all done!
Part 3 – Setting up a desktop environment
There is an X11 server for Android, but as far as I know, there isn’t any window manager for Android yet. So, to get around this little problem, we’ll be running X11 in a framebuffer and use VNC to access this framebuffer.
First, we install the necessary packages. I’m using LXDE, but you could conceivably use something else lightweight like xfce.
apt-get install xvfb x11vnc lxde apt-get --reinstall install xfonts-base
Next, we’re going to disable the logout feature, as it doesn’t work correctly:
dpkg-divert --local --rename --add /usr/bin/lxde-logout cp /bin/true /usr/bin/lxde-logout
Now we need to configure startup files for our X11 session. The following listing shows the script used to start the GUI. This script should be run as the non-root user.
#!/bin/bash # Check if the frame buffer is already running pgrep Xvfb > /dev/null if [ $? -eq 0 ]; then echo "Xvfb is already running. If you want to kill it, use:" echo "pkill -15 Xvfb" exit fi # Launch the X11 frame buffer # This geometry is about half the usable resolution on the Nexus 10 # I choose to half it and zoom, because at full resolution, the UI # elements are way to small to be usable. geometry='1280x750x24' Xvfb -screen 0 $geometry -ac > /dev/null 2>&1 & export DISPLAY=:0 # Give the server a chance to start up sleep 1 # Now start the LXDE session startlxde > /dev/null 2>&1 & echo "Launched LXDE session" # Launch the VNC server x11vnc -localhost -display :0 -forever -usepw > /dev/null
Run this script whenever you want to use the GUI, and use an Android VNC client to open the display at localhost:5900. To kill the GUI, use the following command:
pkill -15 Xvfb
Or just type Ctrl-C in the terminal where it’s running.