Building Android

How to build AOSP for Xperia devices.

The builds for the Xperia XZ will no longer be updated. This guide is severely out of date (as of 2022) and will most likely no longer work. Proceed with caution.

This guide will asume you follow the official Sony build guide, but add some extra tips.

Initialize your build directory

Create a ~/android/build-env folder, cd into it.

Using the repo tool

Download and install the repo tool, either via your package manager or directly from Google. Instead of copying the file, it’s better to symlink:

mkdir -p ~/.local/bin
# Add ~/.local/bin to PATH
echo "export PATH=$HOME/.local/bin:$PATH" >> ~/.bashrc
# Symlink
ln -sf ~/android/.repo/repo/repo ~/.local/bin/repo
# Check:
readlink $(which repo)
# -> /home/builder/android/.repo/repo/repo

Then, run the repo init command:

repo init -u \
  -b android-10.0.0_r39 -g default,-x86,-mips,-darwin,-notdefault

Use the appropriate branch tag instead of _r39. For an overview of currently published tags, see Codenames, Tags and Numbers. Most often, you’ll want to pick the latest Pixel tag, e.g. the Pixel 4.

Ubuntu chroot

Since Google use Ubuntu as a reference build environment, it is advisable you use it as well to avoid incompatibility problems. If you're on Ubuntu already, you can skip this step.

If you’re on another system like Fedora or Arch Linux, you can set up a Ubuntu chroot build environment instead.

Another alternative to nspawn is to use docker, see Sony AOSP on docker.

A build chroot takes about 1-1.5GB of space and will save you from headaches, e.g. when Android modules aren’t compiled against your host glibc.

Install debootstrap, then run this (bionic is the ubuntu version codename for 18.04 LTS):

debootstrap --variant=buildd --arch=amd64 bionic ~/android/ubuntu-android

chroot into the newly created builder system, either via chroot or just using systemd-nspawn (Assuming you created your android build environment via repo init in ~/android/build-env):

sudo systemd-nspawn \
  -D /home/<your-username>/android/ubuntu-android
useradd -m -s /bin/bash builder
# -> No need to add a password, nspawn will log you in automatically

For Android Q, the way .img files are mounted during build has changed, e.g. resize2fs fails with a standard nspawn container.

ln -s /proc/self/mounts /etc/mtab

And add this to the options of systemd-nspawn:

    --bind=/dev/loop0:/dev/loop0 \
    --bind=/dev/loop1:/dev/loop1 \
    --bind=/dev/loop2:/dev/loop2 \
    --bind=/dev/loop3:/dev/loop3 \

(So that enough loop devices are available)

Install required packages

apt install software-properties-common python
add-apt-repository universe
apt update
apt install bison g++-multilib git gperf libxml2-utils make \
  zlib1g-dev zip liblz4-tool libncurses5
apt install openjdk-8-jdk
# Might be needed on Android 10:
apt install flex

# The following dependencies are all optional:
# Faster recompilation
apt install ccache
apt install rsync libssl-dev aapt
# Kernel recompilation:
apt install bc autoconf automake autopoint autotools-dev bsdmainutils gawk \
  groff-base libarchive-zip-perl libpipeline1 libtimedate-perl libtool kmod
# For selinux:
apt install setools python-networkx policycoreutils

Cross-reference the needed packages with the official Sony build guide should this document be out of date.

If you’re building in a chroot: Exit out of the build system by typing exit (and pressing Ctrl-] three times for nspawn), then re-launch your build system and log in as your builder user. If you want to share your own ccache etc. with your build environment, bind the appropriate directories.

sudo systemd-nspawn \
--bind=/home/<your-username>/android/build-env/:/home/builder/build-env/ \
  --bind=/home/<your-username>/.ccache/:/home/builder/.ccache/ \
  -D /home/<your-username>/android/ubuntu-android \

Local manifests

Clone the Sony local_manifests git repo as instructed. For legacy devices, meaning loire and tone, you need to checkout the android-10_legacy branch of local_manifests. For recent devices have to use the master branch.

git clone .repo/local_manifests
cd .repo/local_manifests
# For recent devices:
git checkout master
# For legacy devices
git checkout android-10_legacy

Applying needed patches

Run Your build directory will now be ready for a first build.

Felix: If you want to recreate my own builds, use git clone local_manifests -b 'ix5-customizations-10' instead. After you run, also run This will fetch my newest changes as well.

Prepare build environment

Run source build/ inside your build environment and then lunch aosp_<product>-userdebug.

To find out the product name of your device for aosp_<product>-userdebug, see the Sony Devices overview. E.g. for a single-SIM (“SS”) Xperia XZ, choose aosp_f8331, for a dual-SIM XZ, choose aosp_f8332.

You can also run lunch without any arguments to get a list of all available build targets. They will look like aosp_f83xx-... where f83xx is the model number of your device. You can choose between eng and userdebug builds, see Choose a target.
For your own usage, userdebug is most likely what you want.

The user target is meant for public releases and doesn’t include root. You’ll need to look into ( for Android Q) - and perhaps add it for your device - if you want to use it.

Build kernels

On Android Q, you need to use a script to build the kernel images. Navigate into kernel/sony/msm-4.9/common-kernel and run
If building for a Kernel 4.14 device, go into kernel/sony/msm-4.14/common-kernel and use instead.

Edit build-kernels-{gcc,clang}.sh and change the list of platforms in the script so that it looks like this:

# Override:

This will save you from building the kernel for all Sony devices and only build for the Xperia XZ (“kagura”).

Build & Flash

For legacy non-A/B devices like the Xperia XZ, run this command:

make bootimage systemimage

Newer devices require building for more partitions, like vendor, dtbo, product, vbmeta and others.

In case your build stops at about 90% with this error, most of the time you should be fine by just restarting the build with -j 11.

[ 93% 12745/13686] //frameworks/base:hiddenapi-lists-docs Metalava [common]
FAILED: out/soong/[...]hiddenapi-lists-docs-stubs.srcjar

However, if this still fails, try cherry-picking this patch from LineageOS. See also this reddit post

Then, use fastboot to flash the generated images

cd out/target/product/kagura
fastboot flash boot boot.img
fastboot flash system system.img

For 4.14 devices, also flash the other partitions like vendor, dtbo etc.

Do not forget to flash the appropriate Software Binaries to the oem partition as well!

Optimize the build

A full build will take about two to three hours2 on a beefed-out recent system (Core i7 8th gen 4 cores, 16GB RAM, good SSD). That time can however be cut down for successive builds to around an hour by utilizing ccache and parallelizing the build.

It is recommendable to use a ccache size of around 50-100GB, depending on whether you plan to build different ROMS or for multiple devices.

# On Android Pie and before:
# On Android Q:
# Set ccache size:

Then set the environment variable with export USE_CCACHE=1. This variable needs to be set anew every time you open a new terminal, so it is advisable to put export USE_CCACHE=1 at the end of your .bashrc in your home directory (or /home/builder/.bashrc if you’re in a chroot).

ccache compression might also save a lot of disk space for you. Set export CCACHE_COMPRESS=1 in your .bashrc. This cuts ccache disk usage down to about 5GB per device, but may incur some performance penalty.

On Android Q, you need to install the ccache package and set CCACHE_EXEC in your .bashrc manually to e.g. /usr/bin/ccache. The reason for this is that Google no longer ships a prebuilt ccache with Android.
For more info, see Android Q changes and build/core/

To use parallel compilation, you need to find out how many threads your CPU has via nproc. Then use that number for make -j<nr-of-threads>. For most computers it will be the number of CPU cores times 2, e.g. “8” for a 4-core CPU.

You can also set export WITHOUT_CHECK_API=true in your ~/.bashrc to skip the metalava checks which take up a lot of RAM. Obviously, you should run the metalava checks when you change any Java code yourself, but you can save a bit of time if you skip them for routine builds.

If you're on AOSP Android Q, you need to manually cherry-pick Bring back env flag to skip checkapi. On LineageOS and on current (as of 2019-09-07) master, the CL has been merged already.


If you want to share the fruits of your labor, you can create compressed flashable .zip files. Use make otapackage instead of make systemimage [...]. This will create a flashable zip file in out/target/product/<device-codename> named something like aosp_f8331-ota-eng.hostname-2018-10-19. You can flash this file via adb sideload or use a custom recovery like TWRP.

If you intend to share your builds publicly, you should generate and guard your own set of signing keys. See Signing builds for release for more information.

After you have tested your build on your own device, you can share the file freely, given that it contains free/libre software under the GPL and other free licenses. Anything GPL-licensed also demands you have to divulge your build sources. So put your patches into a git repo and share them with the world.

Speedier development

  • Use m -j $(nproc) to only rebuild a single module. You can then adb push or even adb sync the changes directly to your device
  • Use make bootimage to re-generate kernel and ramdisk only (boot.img)
  • Use adb push or adb sync instead of flashing full images. N.b.: This is only possible with disabled vbmeta.

Going further

To show all available build targets, use make modules.

Show commands used live: make showcommands.

Good references:
eLinux: The Android Build System

  1. Android’s metalava takes up enormous amounts of RAM. It attempts to verify the whole Android API at once and is badly designed. If it is run in parallel to other processes, it will frequently go OOM. By restarting the process, metalava will be the only one running and hopefully succeed. ↩︎

  2. On Android Q, this time goes up to five hours for me. ↩︎

Edit source on Github