On the Ubuntu Touch images we run the Android HAL (Hardware Abstraction Layer) in a container to make use of the binary drivers and some daemons that are needed to drive the builtin hardware of a phone.
Booting by default into the Ubuntu rootfs
To run Android in a container Ubuntu needs to become the default system we are booting to. This is achieved by using a generic Ubuntu initramfs in the phones boot.img instead of the Android supplied one.
The initramfs script that manages the booting lives in the initramfs-tools-ubuntu-touch package. To get a binary initrd.img the ubuntu-touch-generic-initrd package exists, this will always contain the latest binary initrd.img based n the last initramfs-tools-ubuntu-touch package in the Ubuntu archive.
People that are porting their devices to the Ubuntu Touch container model should take into account that Ubuntus upstart is used as init system. Upstart likes to have an actually existing device as /dev/console. Many Android kernels do not provide this as default or even have something like console=none or console=ram hardcoded in their boot commandline arguments. Such drawbacks in the kernel or commandline need to be fixed to be able to use this model properly (here is for example a kernel patch used on the i9100 image to change the behavior of CONFIG_CMDLINE_EXTEND ... now using this option appends the kernel cmdline to the bootloader one instead of pre-pending, this way console=tty1 can be handed over as the last cmdline argument and with CONFIG_VT_CONSOLE and CONFIG_HW_CONSOLE set in the kernel config upstart is now happy)
System Preparation for using the LXC container
Since the Android container is supposed to initialize all hardware (beyond the disk and minimal bits required for booting) it needs to come up as early as possible in the boot process and since android ships its own device handling daemon in form of ueventd, we need to make sure that udev does not intefere with the devices on boot. For this purpose the lxc-android-config package (which is carrying all configuration, overrides, jobs and scripts for running the android container) ships an upstart override job for udev that delays its startup until the container is fully done tinkering with devices. Various udev rules like firmware rules, v4l or alsa rules are overridden as well.
To have similar permissions on the Ubuntu side as we do at the Android side, the lxc-android-config package ships a 70-$devicename.rules file for phones it knows and puts this file in place for udev usage on first boot.
On first boot there is also a script run that creates the proper Android mountpoints and fstab entries in the Ubuntu filesystem, you will find /data, /system, /vendor and /factory (or /efs, depending on the device) mountpoints in the Ubuntu root filesystem.
All first boot handling is done by the lxc-android-boot upstart job.
Booting the Android LXC container
Android does not, like other Linux, jump out of its initrd into a root filesystem on disk. Instead the initrd is actually the rootfs. The Android build that gets created from phablet.ubuntu.com takes care that the android initrd is shipped in /system/boot/android-ramdisk.img from where it gets copied to /boot at install time and on upgrades.
Once the system is ready to fire up a container, upstart sends an event to the lxc-andtroid-config job which uses lxc-start to initialize the preconfigured "android" lxc container config.
The above mentioned "android" config ships in /var/lib/lxc/android with the lxc-android-config package. After the config file has been parsed /var/lib/lxc/android/pre-start.sh is executed. This script unpacks the android-ramdisk.img as the contrainer rootfs and parses its pre-start.d subdir which contains code snippets for temporary or permanent modification of the rootfs. Whole files of the rootfs can be replaced by putting a copy into /var/lib/lxc/android/overrides/ (very useful to work with a modified android init.rc file)
Now that the container is populated with a rootfs lxc-start executes /init inside it and returns to the lxc-android-config upstart job. The lxc-android-config job now emits the "android" event to upstart.
Based on this event delayed services like udev can now get started and apps can start talking to the container through libhybris. Logs inside the container can be read via /system/bin/logcat which (like all other binaries in this path) can just be executed from Ubuntu. The Android properties system is also fully accessible from Ubuntu and getprop/setprop are shipped in the image as management tools and persistent properties can be added to /data/properties if needed.
For further communication between the container and the host the /dev/socket directory from Android is shared across the two systems. Here ofono talks to rild (the vendor shipped binary daemon that manages mobile modems) for example.