Devices - porting to a new device

Ubuntu for devices runs on a variety of mobile devices, such as the Nexus 4 and Nexus 7 2013 (reference hardware) or the bq Aquaris (commercial phone). Due to Ubuntu’s Open Source nature and architecture, it is possible and encouraged to enable other devices to run the mobile OS.

This guide provides all the information required to port Ubuntu to new hardware, and to enable the Ubuntu community to create and host their own ports, with support for Over-The-Air (OTA) updates.

Before getting started

In order to be able to port Ubuntu to a new device, the following prerequisites should be satisfied, nevertheless, you are welcome to follow this guide even if you are attempting your first port with no previous knowledge.

  • Android sources: the device already runs Android and we have access to an Android source tree (preferably the original as with OEM devices but CyanogenMod or independent ports are enough too) and kernel sources for the device. Note that this guide focuses on porting to devices present in the AOSP tree and another version will focus on CyanogenMod based ports.
  • Drivers: you will need the vendor proprietary drivers, which make the GPU and various sensors work. If not explicitly available for download they need to be extracted from a running device.
  • CyanogenMod porting guide: Porting CyanogenMod to a device should be read and understood before embarking on porting Ubuntu Touch, since a lot of the work done requires some knowledge of Android builds and system internals.
  • Boot modes: you need to be familiar with how to boot into various modes (bootloader, recovery…) by pressing volume keys or any device specific method.
  • Unlock and root: the device needs to be unlocked/rooted in order to allow flashing custom images. A lot of preparation work is common with attempts to port CyanogenMod or other custom Android ROMs.

A few more tips

The XDA developer forums should be searched and consulted for anything non-Ubuntu specific.

Also always refer to sources of a workingimplementation, the official one being the Nexus 4 port.

Terminology

Throughout this guide, some new terms might be introduced. This list should help you getting started understanding what they are:

  • AOSP: the Android Open Source Project hosting the original Android source code for a selection of devices.
  • CyanogenMod: an open-source OS based on Android.
  • Recovery image: an alternative boot mode, independent of your operating system where you can perform advanced recovery and maintenance operations.
  • ClockworkMod Recovery: a custom recovery option for Android devices that can be used as a point of entry to replace Android.
  • AppArmor: a Mandatory Access Control (MAC) system which is a kernel enhancement to confine programs to a limited set of resources, such as reading from and/or writing to a specific list of folders or files.
  • Click package: the packaging format used for apps on Ubuntu, confined by AppArmor.
  • Mir: the display server used by Ubuntu, it's a next-generation replacement for X11.
  • ADB: a client-server program used in Android application development. It is often used to manage and access shells on Android-based devices.
  • Unlock: the action of disabling the software limiting the phone to a single operator's network
  • Root: the action of enabling administrative access ("root" access) to the OS.

Architectural overview

To rapidly support a wide range of devices, our architecture reuses some of the drivers and hardware enablement available for Android.

As a consequence, at the current images you'll find some of the Android services running at the device, separated in a lxc container, providing all the basic services needed for Ubuntu to run fully accelerated (media, graphics, modem, etc).

For quick reference, these are the current components used from Android:

  • Linux Kernel (stock Android kernel provided by the vendor, with a few changes to support some extra features needed by Ubuntu, such as Apparmor)
  • OpenGL ES2.0 HAL and drivers
  • Media (stagefright) HAL, to re-use the hardware video decoders
  • RILD for modem support
  • Android Camera HAL and Camera service

As Ubuntu is running as the main host on top of an Android kernel and the communication between the Android services and HAL happens via Binder, Sockets and libhybris.

Other than the very basic services (needed to re-use the binary blobs already available), the rest is just pure Ubuntu goodness (we don't have dalvik running, for example).

Toolchain and build system

A toolchain is a set of distinct software development tools that are linked (or chained) together by specific stages, and are used to build each part of the stack for a final image running Ubuntu - such as GCC (compiler), binutils (tools) and glibc (runtime library).

Toolchain considerations

It is preferred to build everything using the cross toolchain in Ubuntu. Android source trees ship with their own copy of gcc/g++, but unless there are errors when building with the Ubuntu tools, use the Ubuntutools.

Build system

Make sure you are familiar with the Android build system and can build a booting Android image from sources and vendor blobs. If you are not, look closely at the following “Set up your development environment” section.

Set up your development environment

Whether you want to build Ubuntu for the currently supported Nexus devices or want to port it to a new target, you need to set up your working environment to build Android from source. This setup is more or less the same whether you are building AOSP or a project using a part of its sources.

You can find all the needed Android specific git repositories at code-review.phablet.ubuntu.com. This is a working gerrit server with everything needed to build the Android images used by Ubuntu Touch. The reference tree in there is based on AOSP (4.4.2 specifically), so make sure your device specific repositories are compatible with AOSP at least.

For any Android related project at our git server, you'll find a branch named phablet-4.4.2_r1. This branch contains a static known git HEAD and the required changes needed for Ubuntu, including our custom Androidmanifest.

The first step is to add the phablet-tools PPA and install the phablet-tools package. Open a terminal and type:

$ sudo add-apt-repository ppa:phablet-team/tools
$ sudo apt-get update
$ sudo apt-get install phablet-tools

Then, install all the other packages you will need for the build by running this command:

$ sudo apt-get install git gnupg flex bison gperf build-essential \
   zip bzr curl libc6-dev libncurses5-dev:i386 x11proto-core-dev \
   libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 \
   libgl1-mesa-dev g++-multilib mingw32 tofrodos \
   python-markdown libxml2-utils xsltproc zlib1g-dev:i386 schedtool \
   g++-4.8-multilib

From this point on, most of the work is done by the phablet-dev-bootstrap command (which is, amongst other things, a wrapper over the Android “repo” command used to manage an Android source tree).

Create a new “phablet” directory. That will host a local repository containing most of the Android and Ubuntu pieces. Then run phablet-dev-bootstrap to sync that repository using a custom manifest.

$ mkdir phablet
$ phablet-dev-bootstrap phablet

This step will take a long time (several hours), as it is downloading around 15GB of Android sources.

Recommended: build your first Ubuntu image for a Nexus 4

This is an optional step, but an important one for newcomers. It will ensure your environment is correctly set up. You are going to build your first Ubuntu image for a Nexus 4 before building your port.

First, let’s speed up the incoming build process by enabling caching. Run this command on a terminal:

$ export USE_CCACHE=1

Then, source your path with a set of android build tools. Run this command now:

$ . build/envsetup.sh

See the list of build targets by running the lunch command

$ lunch
      You're building on Linux
      Lunch menu... pick a combo:
        1. aosp_arm-eng
        2. aosp_x86-eng
        3. aosp_mips-eng
        4. vbox_x86-eng
        5. mini_armv7a_neon-userdebug
        6. mini_x86-userdebug
        7. aosp_grouper-userdebug
        8. aosp_tilapia-userdebug
        9. aosp_deb-userdebug
        10. aosp_flo-userdebug
        11. aosp_hammerhead-userdebug
        12. aosp_mako-userdebug
        13. aosp_manta-userdebug

Choose the device you want to build for (in this case, number 12 : aosp_mako- userdebug)

As the last step, use the make command to build the final image.

$ make -j4 # Or any other amount of threads

And that’s it! If everything went well, you should now have a set of flashable images (boot, system, recovery) in the out/target/product/mako folder.

Flashing the image

After the build out/target/product/mako will have the device specific build artifacts such as boot.img, system.img, recovery.img which can be flashed using fastboot to the respective partitions like:

$ fastboot flash boot boot.img
$ fastboot flash recovery recovery.img

As our Ubuntu image is not built by the Android build system, the best approach is to just flash it directly using rootstock, like described bellow (from bootloader):

$ fastboot boot out/target/product/mako/recovery.img
$ bzr branch lp:project-rootstock-ng
$ cd project-rootstock-ng/
$ ./rootstock-touch-install vivid-preinstalled-touch-armhf.tar.gz out/target/product/mako/system.img

You can find the latest Ubuntu rootfs image at http://cdimage.ubuntu.com/ubuntu-touch/daily-preinstalled/current/vivid-preinstalled-touch-armhf.tar.gz.

Enabling a new device

If you're repository is compatible with the AOSP tree, it's just a matter of adding the device specific git repositories and the vendor files (usually blobs that you need to extract from the original android image). Once that is done, you should be able build a small android system image that can be used by Ubuntu Touch in order to make it fully compatible with the device.

Device

Add your device specific git repositories under phablet/device ("phablet" is the repository you cloned by following the previous build example and/or Touch/Building).

Make sure it's respecting the format used by AOSP (device/<vendor>/<device name>).

Hardware

Add your hardware specific git repositories under phablet/hardware.

Make sure it's respecting the format used by AOSP (hardware/<vendor>/<component>). As an example:

$ ls hardware/qcom/audio/
Android.mk  hal  legacy  visualizer  voice_processing

Vendor

Add your vendor specific binary blobs under phablet/vendor.

Make sure it's respecting the format used by AOSP (hardware/<vendor>/<component>).

$ ls vendor/asus/flo/
BoardConfigPartial.mk  BoardConfigVendor.mk  device-partial.mk  device-vendor.mk  proprietary

Retrieving the proprietary blobs from Android

Ubuntu Touch Preview uses some pre-compiled binary drivers from the Android layer for rapid enablement of devices. These are referred to as binary or proprietary blobs, as their source code is not available for the build, and are included in binary form.

Since we use AOSP as a base, for supported devices all you need to do is to download and extract and run as mentioned in the downloads from https://developers.google.com/android/nexus/drivers

Device-specific changes

Adapt the Ubuntu kernel: building for an Android device

Kernel config

You can find your kernel config at kernel/[manufacturer]/[codename]/arch/arm/configs/ cyanogenmod_[codename]_defconfig. Please double check that it is indeed the default config file name indevice/[manufacturer]/[codename]/*.mk (look for the TARGET_KERNEL_CONFIG variable).

We need to enable Ubuntu-specific kernel config options. These config changes are essential, as without them the Android container won't start. The tool under “kernel” in this git repo can do it automatically.

check-config <defconfig file> -w will overwrite the existing configuration with appropriate values for Ubuntu.

Alternatively, the script itself contains the list of configuration values you need to enable or disable.

For debugging while hardware bringup, you may also want to enable

CONFIG_PANIC_TIMEOUT=5 CONFIG_ANDROID_RAM_CONSOLE=y

The Ubuntu boot image and initramfs may not work at first so these two help read /proc/last_kmsg in case of a boot loop due to a kernel panic from the recovery image - which should work already, being more or less standard Android based.

Kernel patches

AppArmor 3.0 patches are essential for click packages (which contain the Ubuntu apps) to run. In general, many security-related and Ubuntu SAUCE patches are going to be expected by the Ubuntu userland. In any case, the phone will boot even without AppArmor, so these can be added later on as the porting progresses.

These patches should be picked from a known good Ubuntu kernel tree, such as the mako (Nexus 4) branch in the official Ubuntu kernel repo:

http://kernel.ubuntu.com/git?p=ubuntu/ubuntu-utopic.git;a=shortlog;h=refs/heads/mako

udev rules

udev is the permission manager for device nodes and supplies the system with device events.

Make sure device nodes are in place by generating an udev rules file based on Android's ueventd file (and possibly manual additions). Most importantly, pay attention to file permissions.

The GPU and drivers will only work once their device nodes are set up.

You can create the udev rules file by invoking the following command while Ubuntu is booted.

$ adb shell cat /var/lib/lxc/android/rootfs/ueventd*.rc|grep ^/dev|sed -e 's/^\/dev\///'|awk '{printf "ACTION==\"add\", KERNEL==\"%s\", OWNER=\"%s\", GROUP=\"%s\", MODE=\"%s\"\n",$1,$3,$4,$2}' | sed -e 's/\r//'

If you are unsure of the result, have a look at the udev.rules file in /device/VENDOR/DEVICE/ubuntu to get a grasp of what is expected.

AppArmor

AppArmor is an integral part of Ubuntu Touch and is required to use click packages. You are able to use Ubuntu without enabling AppArmor, but you will not be able to use click packages and the port will not be complete without backporting the AppArmor v3 patchset to older kernels. AppArmor has been backported to the following kernels in the Ubuntu archive already:

If you need it for another Kernel, the AppArmor porting process is explained in depth in the AppArmor Portingguide.

Once the AppArmor patchset is working, you will likely need to add hardware- specific AppArmor rules for your device. You'll know this is the case if when running an application it shows an AppArmor denial in /var/log/syslog -tail -f /var/log/syslog | grep DENIED is very handy when troubleshooting policy).

To make this easier, packages can drop AppArmor snippets into /usr/share/apparmor/hardware/. An entry for graphics hardware will almost certainly be required for click packages to work on the device. The policy consults the following directories:

  • /usr/share/apparmor/hardware/graphics.d: specific graphics hardware access. Used by AppArmor templates
  • /usr/share/apparmor/hardware/audio.d: specific audio hardware access. Used by the audio policy group (due to the use of pulseaudio, this may not be needed for your device)

Though any package can ship the policy, it makes sense for lxc-android-config to ship it since it also ships the udev rules. For example, if porting the HTC Desire Z (vision), you might ship the following:

$ cat /usr/share/apparmor/hardware/graphics.d/htc-desire-z-vision
  # HTC Desire Z (vision)
  /dev/kgsl-2d0 rw,
  /dev/genlock rw,
  /sys/devices/system/soc/soc0/id r,

Typically you specify the path to the device followed by 'r' for read access or 'rw' for read and write access. Simple globs and AARE (AppArmor regular expressions) are also possible. See man apparmor.d for details. When developing your policy, be sure to run sudo aa-clickhook -f before running your app to regenerate the policy.

Please see DebuggingApparmor for more information on how to debug AppArmor policy.

Testing AppArmor

You can test that AppArmor is functioning correctly by:

  • viewing the output of the aa-status command
  • manually installing a click app doesn't show any AppArmor errors (sudo click install --force-missing-framework --user=$USER ./foo.click)
  • launching the manually installed click application works
  • when running, the click application is confined as seen with aa-status. Eg (be sure there are two entries: the first is that the profile is loaded and the second shows a particular pid is running under this profile), eg:
$ sudo aa-status|grep hello-world
ar.com.beuno.hello-world_hello-world_0.1
ar.com.beuno.hello-world_hello-world_0.1 (24622)
  • install/launch hello-world from the Ubuntu appstore and launch it via 'Installed applications'. It should run and aa-status should show it is confined
  • launch the twitter webapp via 'Installed applications' (preinstalled on the device, otherwise get from the Ubuntu appstore). It should run and aa-status should show it is confined

While porting, it might be useful to disable AppArmor:

  • system-wide: boot with apparmor=0
  • only for click packages: adjust the desktop file for the click package in ~/.local/share/applications/<click>.desktop to not use aa-exec and/or adjust /usr/share/upstart/sessions/application-click.conf to not use 'apparmor switch'

IMPORTANT: disabling AppArmor for click packages in this manner means that you are disabling important security protections and allowing untrusted, unreviewed arbitrary code to run on your system. While this may be fine while developing the port, it is not enough to finish the port.

Brightness indicator

Due to the incorrect default permission at the sys brightness file, a change is needed to allow any user to change the display brightness. We need to chmod it to the proper permissions.

You can usually find the permission settings needed at the device init file, which is usually available under device/[manufacturer]/[codename]/init.[codename].rc.

The changes should look like the following:

      1 device/samsung/p3100$ git diff
      2 diff --git a/init.espresso.rc b/init.espresso.rc
      3 index cae5772..4e7df71 100755
      4 --- a/init.espresso.rc
      5 +++ b/init.espresso.rc
      6 @@ -224,6 +224,7 @@ on post-fs-data
      7
      8 #Change permission for backlight and lcd
      9        chown system system /sys/class/backlight/panel/brightness
     10 +       chmod 0666 /sys/class/backlight/panel/brightness
     11        chown system radio /sys/class/lcd/panel/lcd_type
     12        chown system radio /sys/class/lcd/panel/lcd_power

Build changes

main.mk

The main build file needs to be checked if updates are required to it to support new drivers or parts of the build not used before, its path is: build/core/main.mk

The part of interest are the subdirs included in the build and if special treatment for devices need to be made. For example, make sure the new vendor subdirs are added!

Put components together

Try building and booting a recovery image, preferably ClockworkMod as the phablet codebase uses. This way all the changes added by Ubuntu to recovery including branding and image upgrade functionality will work. Use the newly built Ubuntu kernel for the recovery image.

Add Ubuntu specific components to the tree (hybris, platform-api, etc). See them under the ubuntu/ directory in your source.

Building

The image

Like in the previous Nexus 4 example, lunch and make commands will create an “out” directory containing flashable images. You can run the same set of commands as for the Nexus 4 build:

$ . build/envsetup.sh
$ lunch # (Pick your device target)
$ make -j4

Push to your device with rootstock-ng

At the end of the build process a couple of .img files will be generated in out/target/product/<codename>. You can use rootstock-ng to easily push it to your device.

Rootstock-ng is a set of scripts to create an Ubuntu rootfs (or ISO) locally on your PC like it is done by the Ubuntu cdimage server. You can have a look at its README for more info on what is happening under the hood.

$ bzr branch lp:project-rootstock-ng [rootsock_trunk_path]
$ ROOTFS="utopic-preinstalled-touch-armhf.tar.gz"
$ wget -c "http://cdimage.ubuntu.com/ubuntu-touch/daily-preinstalled/current/$ROOTFS" -O "$OUT/$ROOTFS"
$ $ROOTSTOCK_DIR/rootstock-touch-install "$OUT/$ROOTFS" "$OUT/system.img"

Clean up

Optionally remove components from the tree not used by Ubuntu (chromium, dalvik) if they take up too much space and you want to share the tree with others. These components should not affect build time and image footprint if the previous step was complete and the build/ directory was patched to only build what is necessary.

Change device specific files, for patches which are unlikely to apply cleanly, this include fstab, various init.rc scripts.

Once the device boots into an adb shell you can try working on getting the rest of the system to work. See the Debugging section below.

The graphics stacks is likely to need debugging as well. If it is a device that has not yet been used with Ubuntu before, even Mir may need patches. Understanding gralloc and the rest of the graphics components of the Android HAL is useful here, and can be one of the most difficult parts of the port. If needed, the Mir Hackingguide will help you getting started with Mir.

Setting up an image server

Now that you have your own Ubuntu port, you probably want to serve over-the- air updates to devices and for that, you need an image server: have a look at this step by step explanation by Stéphane Graber to learn how to create one.

Debugging

Initrd

If you encounter initrd issues while booting, you can add the following line to your kernel boot arguments

break=top

Alternatively, you can replace "top" by another one of the stages below:

  • modules
  • premount
  • mount
  • mountroot
  • bottom

This will open an adb shell and pause execution of the initrd, so that you can look around. Unlike the usual behavior of breakpoints on the desktop, you cannot resume execution by exiting the adb shell.

You can also add “debug” to the kernel boot argument. This will cause a log to be recorded in /run/ with the progress of the initrd booting process.

Working in the environment

Networking

The simplest solution, assuming that you have a UI available is to use the networking indicator on the device.

An alternate means of configuring networking is via the phablet-network tool, which is part of the phablet-tools package.

With your Ubuntu device connected to your host computer, simply run phablet- network from your desktop to copy an active Network Manager system settings file to the device. The script also has some extra options which cause the tool to install network-related packages such as iw and openssh-server.

If you have difficulty enabling the wifi drivers on your platform, you can still get network on your phone with reverse USB tethering over adb. See AdbNetworking for details.

Copying files to the phone

This is required if you are not working from the Android source tree. As fastboot and adb should be built as part of the system build for the host part, they should be in your path and located here:

out/host/linux-x86/bin/

If not installing a personal build and not porting using Trusty (or more recent), make sure you have the phablet-team PPA added, if not, run add-apt- repository ppa:phablet-team/tools

Then make sure to install adb and fastboot. You can do so by installing the following packages:

$ apt-get install android-tools-adb android-tools-fastboot

To get packages to your device, execute the following from your desktop:

$ adb root
$ adb push [filename|directory name] [ubuntu path]

Screen Pixel Ratio

We have 2 important variables that define the pixel ratio behaviour of the system and the applications, ie. how they visually scale. Look at /usr/bin/ubuntu-session for GRID_UNIT_PX and QTWEBKIT_DPR.

When adding the correct pixel ratio for a new port, first use the method below to calculate the desired DPR, and create a device specific config file which ubuntu-session can load at run time.

The number of pixels per grid unit (GRID_UNIT_PX) is specific to each device. Its goal is to make the user interface of the system and the applications of the same perceived size regardless of the device they are displayed on. It is primarily dependent on the pixel density of the device’s screen and the distance to the screen the user is at. That second value cannot be automatically detected and is based on heuristics. We assume that tablets and laptops are the same distance and that they are held at 1.235 times the distance phones tend to be held at.

A reference device has been chosen from which we derive the pixels per grid unit for all other devices. The reference device is a laptop with a 120 ppi screen and the pixels per grid unit is set to 8 px/gu.

Device Form Factor Resolution Density Pixels / grid unit
‘Normal’ Density Laptops Desktop * 96-150 8 px/gu
‘High’ Density Laptops Desktop * 150-250 16 px/gu
Samsung Galaxy Nexus Phone 1280x720 316 ppi 18 px/gu
LG Nexus 4 Phone 1280x768 320 ppi 18 px/gu
Samsung Nexus 10 Tablet 2560x1600 299 ppi 20 px/gu
Asus Nexus 7 Tablet 1280x800 216 ppi 12 px/gu
Asus Transformer Tablet 1280x800 149 ppi 10 px/gu

There is no way for the system to dynamically identify the correct pixel ratio for the device, which as a side effect things might be bigger/smaller than expected. For each device you will have to visually verify the quality of the result and adjust the number if necessary. If unsure, send screenshots and screen specifications of the device to the Canonical design team.

To create your device specific configuration, first identify the ro.product.device android property used by your device (check the /system/build.prop file from your port or from the original Android image).

Then with the desired DPR, create a file at /etc/ubuntu-touch-session.d adding your custom GRID_UNIT_PX and QTWEBKIT_DPR variables, also specifying the default form factor you want, such as:

$ cat /etc/ubuntu-touch-session.d/manta.conf
GRID_UNIT_PX=20
QTWEBKIT_DPR=2.5
FORM_FACTOR="tablet"

Need help? Get in touch

Porting an OS to a new device is a broad subject and this guide doesn't pretend to be exhaustive on that topic. If you need more help on specific topics, come meet the Ubuntu teams and other like-minded people on IRC : #ubuntu-touch on Freenode.

You should also probably join our mailing list by adding yourself to the ubuntu-phone team on Launchpad and enabling the team mailing list in your Launchpad options.

If you want to report a bug or suggest changes to this guide, you can do it here.

Troubleshooting

Cannot start ADB without graphics

The ADB service (adbd) lives in the Ubuntu rootfs instead of in the Android container. For security reasons, the adbd binary shipped with Ubuntu checks if the device's screen is unlocked and if there is a password set for the phablet user. It will only start when both requirements are met.

This can present a challenge for porting: if the UI does not come up for some reason, there is no lock screen to check against, and thus adbd will never start.

As a temporary workaround to enable porters to use ADB until any graphical issues have been fixed, there's an unlocked adb binary that can be used instead of the preinstalled one. To use it:

  • Download the unlocked adbd binary
  • Copy it into the /usr/bin/adbd directory in the Ubuntu rootfs of the device being ported
  • Run the following command to bring up the adbd service as soon as the system switches to the rootfs echo "start on startup" >/etc/init/android-tools-adbd.override