Category Archives: Linux

Chroot Ubuntu 14.04 on Android (Nexus 10)

I previously had this tutorial for Ubuntu 12.10 on a Padfone and a Nexus 10. I’ve updated it for Ubuntu 14.04.

In this guide, I used http://ports.ubuntu.com/ubuntu as the repository for the bootstrap. However, I ran into some problems and had to retry the installation several times, and ports.ubuntu.com was somewhat slow for me, so I mirrored the trusty armhf repository at http://jtpool.tan-ce.com/ubuntu. I give no guaranties about its uptime or freshness, but if you’re near me (Singapore), you could give it a try to speed things up.

In general, Ubuntu 14.04 doesn’t install as cleanly as 12.10 did, but I believe it should still work. In future, I may have to switch to another distro. Perhaps Arch.

This tutorial documents the steps I went through to get a working Ubuntu system (with LXDE) on my Android device, a Nexus 10. 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.

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.

References
The following were webpages that I found useful. Some even have almost all the steps you need to do this.

Requirements

  • A rooted tablet
  • 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 Linux environment (Might work even on a live CD)

You will also likely need busybox on your tablet

Part 1 – Creating the bootstrap image
We are going to create a filesystem image to contain the entire Ubuntu 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:

cd /media/BigDisk

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 trusty 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. For me, my image was in /sdcard/lubuntu

su
cd /sdcard/lubuntu

Now, mount the filesystem, setup a minimal environment, and chroot into it. You will need busybox in order to chroot. In my tablet, 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

Now that we’re in, let’s finish up the installation:

/debootstrap/debootstrap --second-stage

You’ll notice that a package failed to install – that’s udev. udev in Ubuntu 14.04 performs several checks and will fail, causing apt to fail to install the entire package. To neuter udev so that we can install the rest of the system, run the following command:

dpkg-divert --local --rename --add /etc/init.d/udev
cp /etc/init.d/udev.distrib /etc/init.d/udev

Now, open /etc/init.d/udev in vi, and right after “### END INIT INFO“, enter this line:

exit 0

We also need to neuter initctl so that the fact that upstart doesn’t work does not give us lots of warnings and failures.

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 8.8.8.8' > /etc/resolv.conf

Next, open /etc/apt/sources.list in an editor and replace the contents with this:

deb http://ports.ubuntu.com/ trusty main universe multiverse restricted

deb http://ports.ubuntu.com/ trusty-updates main universe multiverse restricted

deb http://ports.ubuntu.com/ trusty-security main universe multiverse restricted

Now we’ll update the apt indexes, upgrade to the latest versions of all the packages, and finish installing the packages that failed to install during the debootstrap.

apt-get update && apt-get upgrade -y

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 install ubuntu-standard vim tmux openssh-client openssh-server
apt-get clean
rm /var/run/reboot-required*

If you followed me and installed ubuntu-standard, you’d get another failed package: libpam-systemd. To solve this problem, we’re going to create a dummy init.d script. Create a file at /etc/init.d/systemd-logind with 0755 permissions (executable by all). In this file, enter the following:

#!/bin/sh
exit 0

Then, let apt complete the installation by running “apt-get upgrade -y”.

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:

sh enter_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 are two ways to run a GUI from inside the chroot. Previously, I ran an X11 server in a framebuffer and then used VNC to access it. However, there is now a pretty good X11 server available for Android. I generally use this app. Then all you need to do is install the app, install the “lxde” metapackage, and run “DISPLAY=:0 startlxde”, assuming the app started on display :0.

Note: When I installed lxde, it re-installed libpam-systemd, and two more packages failed. When I purged it (again), those two packages were uninstalled along with it.

That said, if you’re still interested in the VNC on X framebuffer method, take a look at the old article.

Android 4.4 (KitKat) kernel for Nexus 10

Not sure what this is about? Read this post first.

Update (26 Nov): I’ve updated this twice now, once to add several 3G modem/dongle drivers, and the last round to put back the out of tree driver for my USB Ethernet stick.

Update (14 Dec): I’ve added a kernel for those of you running KOT49H (ie. Android 4.4.2)

The kernel sources were finally released yesterday morning (in my timezone). I’m guessing this happened at the same time KRT16S became available. Anyway, I’ve compiled my tweaked version of the kernel for the Nexus 10. Download it here:

And yes! I’ve finally gotten round to making an update.zip of the boot image. The zip flashes the boot partition, copies the kernel modules into /data/kernel_modules/<kernel name> and symlinks /system/lib/modules/<kernel name> to /data/kernel_modules/<kernel name>. The symlink is necessary because, at least on my device, there is very little space left on /system, and my modprobe is configured to look in /system/lib/modules.

Note that I’ve only tested the update.zip on CWM Recovery 6.0.4.3. In any case, you should always be able to flash this using fastboot. To flash it manually with fastboot, just extract the zip – the kernel modules and boot image are inside.

I believe the most common drivers for 3/4G USB dongles are already compiled in. Some others many be present as loadable modules.

PPP Widget Note:

I don’t personally use PPP Widget, but I realised many of you are using 3/4G USB dongles with your Nexus. Nigel (in the comments below) pointed out that PPP Widget does not set the DNS servers in Android correctly all the time, and you may be using DNS servers from the last time your tablet was connected to WiFi. The fix for this is disable WiFi before connecting with PPP Widget.

Android root shell

UPDATE (13 Jan, 2014): koush has merged my pull request into Superuser, so his su app should have better terminal handling now.

As I mentioned in the last post, I’ve had some annoyances with the root shell since the upgrade to Android 4.3. To recap, from Android 4.3, the traditional way of using su on Android no longer works because Android’s zygote process drops many capabilities, and the entire /system partition is mounted nosuid. SuperSU solves this by proxying requests to a daemon who is not a descendant of zygote, but this messes up terminal window sizing. Additionally, I’ve since switched from SuperSU to koush’s Superuser, which actually makes the root shell even worse, as it doesn’t seem to handle the terminal properly, leading root applications to believe they are connected to a pipe rather than a terminal.

I did several experiments and spent some time reading SSH’s source code. Then I clobbered together several utilities and managed to produce the exact result I wanted: A shell which behaves properly, terminal-wise.

From there I made a daemon which does nearly the same thing as SuperSU’s and Superuser’s su daemon, as well as a complimentary client for requesting applications to be run as root by the daemon. The daemon (pts-daemon) is launched at boot time via init, and is therefore already running as root with all Linux capabilities intact.

When you run the client (pts-shell),

  • pts-shell establishes a unix socket connection to pts-daemon.
  • pts-shell asks the user for the password and sends it along to the pts-daemon via the unix socket.
  • pts-daemon responds with an “OK” or “Authorization Failed”. If all is OK, we continue.
  • pts-client opens a pseudo-terminal device and tells pts-daemon the name of the newly created pts device, along with a path and command line arguments to an application to run (for example, /system/bin/sh).
  • pts-daemon forks a child process. This child process will then become a process group leader (setsid), open the pseudo-terminal device pts-client created, make that pseudo-terminal device its stdin/stdout/stderr, then finally exec the application pts-client wanted to be run.
  • Meanwhile, pts-client changes the termios settings on its stdin, doing things like putting it in raw unbuffered mode and disabling echo. Then it starts pumping bytes between its own stdin/stdout and the pseudo-terminal. In addition, it also traps SIGWINCH, sending TIOCSWINSZ to the pseudo-terminal with the new row and column count whenever it receives that signal.

pts-client is able to receive SIGWINCH because it’s attached directly to the terminal emulator. To clarify, this means that pts-client should not be run from inside a root shell, as this would completely negate the whole reason for using it!

All in all I’m quite happy with how this has worked out. You can see the code on GitHub. If you want to try it out yourself, this is how you can install it:

  • Download the update zip and load it using ClockworkMod or TWRP.
  • In a root shell, run pts-passwd to set the password for the first time. (You can run this again to change the password.)
  • From a regular (non-root) shell, run pts-shell <path to application> any time you want a root shell. (eg. “pts-shell /system/bin/sh”)

I’ve only tested it on a Nexus 10 with koush’s Superuser app, although I’m pretty sure it should work with SuperSU’s as well. Do let me know how it works out for you, if you try it.

Android 4.3, su proxy, loadable modules

Updating to 4.3 (the manual way)

Google released Android 4.3 a few weeks ago, and last week, the OTA for my Nexus 10 finally arrived. I eagerly upgraded, and stupidly forgot to backup my internal storage first. It somehow completely slipped my mind that I made several changes to /system (added kernel modules, threw in busybox, bunch of custom scripts). The update restarted the phone into recovery where it got stuck at the “updating” animation. Hoping that it was just taking very long, I went to lunch, but it was still stuck when I got back.

In the end, I installed TWRP. I had ClockworkMod, but adb did not work with CWM. Using adb shell and adb pull, I backed up my internal storage (including my precious Linux chroot) and then turned to performing a manual upgrade.

First, I got the factory image package from Google (Android 4.3 is build JWR66V) then I extract the compressed tarball which contains an img file (bootloader), a zip file (OS partition images), and some scripts. The scripts erases the entire device, and I didn’t really want to start over from scratch, if possible.

So, I erased the boot, cache, recovery, and system partition:

fastboot erase boot
fastboot erase cache
fastboot erase recovery
fastboot erase system

(Note that I did not erase userdata, the “internal storage” partition.)

Next, I flashed the new bootloader, then proceeded to reboot the bootloader:

fastboot flash bootloader bootloader-manta-mantamd03.img
fastboot reboot-bootloader

After the new bootloader started, I initiated the OS update:

fastboot -u update image-mantaray-jwr66v.zip

Note: The provided flash-all.sh script uses “-w”, which erases all user data. “-u” keeps user data.

I’ll admit, I was a bit surprised, but the update went flawlessly. I now have Android 4.3 with all my apps and data intact. I’m guessing that I was rather lucky because this update had no problem with my existing userdata, or perhaps my earlier failed attempt at the OTA update already converted what needed to be converted. Unfortunately, I don’t know the update process well enough to say for sure.

Where’s my root?

As expected after an update, root was gone. I replaced my su binary (using TWRP and adb) and I thought things would go back to the way it was. It didn’t. The old su binaries completely do not work.

Android 4.3 upped its security quite a bit. Two things affect root users: the addition of the nosuid flag to the system mount, and the removal of many capabilities from most of the processes running in the system, especially CAP_SYS_ADMIN, CAP_SETUID, and CAP_SETGID. Fortunately, Chainfire figured out the way forward with 4.3. All I needed to do was to install the new version of SuperSU. He explains what’s changed and why in these three posts.

So, with root restored, I have access to my chroot back, and I can load kernel modules again, right?

No loadable modules?

Nope. The kernel in 4.3 is compiled without loadable module support. I re-compiled the kernel from source, re-enabling loadable module support, and throwing in a few other options which tickled my fancy. (I think there’s 802.1 D and Q support, and one or two smaller drivers compiled in – I don’t really remember everything I changed.)

I found a tool (AnyKernel, I think) which was supposed to allow me to flash any kernel I want using the update.zip method. Unfortunately, that tool (or at least the version of it which I found) was meant for Android 2.3 or below, and the API has changed in ICS.

In the end, I took the original boot.img from the stock firmware image, unpacked it (using this tool – it says Xperia boot tools, but it worked for my Nexus 10 too), replaced the kernel, then repacked it. After doing that, I flashed my new boot.img using fastboot. That seemed to do the trick.

Update: I’ve added support for some 3G dongles (usb_wwan, option, and ppp_async), but I made a mistake and had to recompile, so you’ll have to reflash the boot.img. And since you had to reflash anyway, I made those three modules and cifs built-in, since they’re not large modules anyway.

For the Nexus 10 (ie. manta) there are two files:

  • tan-ce_manta_xxxxxx_y.z_boot.img – basically stock boot.img with my kernel (which is for the Nexus 10, with Android 4.3, build number JWR66V or JWR66Y). Essentially stock + loadable modules and a few other options and drivers enabled.
  • tan-ce_manta_xxxx_y.z_modules.zip – Collection of the following loadable kernel modules:
    • auth_rpcgss.ko
    • cryptoloop.ko
    • dm9601.ko
    • dm9620.ko (This isn’t in mainline, but my USB to Ethernet dongle uses this.)
    • ebt*.ko – ebtables support
    • exportfs.ko
    • gl620a.ko
    • l2tp_core.ko – support for L2TP tunneling
    • lockd.ko
    • mcs7830.ko – USB ethernet
    • nbd.ko
    • nfs_acl.ko
    • nfsd.ko
    • nfs.ko
    • plusb.ko
    • rndis_host.ko
    • scsi_wait_scan.ko
    • smsc75xx.ko
    • smsc95xx.ko
    • sunrpc.ko

Several useful modules, like ntfs and cifs, are not listed there – this is because I made them built in. Even so, the kernel is only 800KB larger than pure stock.

You can download the files below:

NEW: Update for Android 4.4 (KitKat), KRT16S: tce-manta-KRT16S_boot.zip
See this post for more info.

Also, some have asked me to provide a Nexus 7 kernel as well. I tried to compile them, but I’m told those kernels are only good for boot-loops… If you’re using a Nexus 7, perhaps you can try this kernel. I understand it has support for 3G modems now.

One remaining TTY issue…

I haven’t quite figured this one out yet, and if I do, I’ll post about it. Since su is now being run via proxy, when using it in the Terminal Emulator, all su-ed commands are no longer connected to the pseudo-terminal directly attached to Terminal Emulator.  As such, ncurses apps, and even the line discipline is sometimes messed up. I’ve been toying with the idea of establishing my own pts sessions directly to the shell, but I haven’t quite figured it out yet.

Update: For now I’m using a script to send stty commands for the screen size to the appropriate /dev/pts device. When launching a root shell, I pass it an argument which is the name of the “real” pts device in use by terminal emulator. The script, when run form withing the root shell, reads the screen size from the “real” pts device and configures it’s controlling pts to be the same. You’ll have to re-run the script every time the screen size changes, but it works as a temporary workaround. Here’s the salient parts of the script:

UPDATE (2): I’ve figured out how it works!

# CTTY is an environment variable containing the "real" user facingpts device

if [ "$CTTY" = "" ]; then
    echo "CTTY not set"
    exit 1
fi

# Set the rows and columns
SIZE=`stty -F "$CTTY" size`
ROWS=`echo "$SIZE" | cut -f 1 -d ' '`
COLS=`echo "$SIZE" | cut -f 2 -d ' '`
echo "Setting screen size to $ROWS x $COLS"
stty rows "$ROWS" cols "$COLS"

Chroot Ubuntu 12.10 on Android (Asus Padfone, Nexus 10)

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.

References
The following were webpages that I found useful. Some even have almost all the steps you need to do this.

Requirements

  • 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:

cd /media/BigDisk

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:

/debootstrap/debootstrap --second-stage

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 8.8.8.8' > /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:

sh enter_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.