This project enables using Nix to build Android ROMs, currently targeting Pixel 1-4(a) (XL) devices. Robotnix uses a NixOS-style module system for customizing various aspects of the build.
Some optional modules include:
- Vanilla Android 10 AOSP support
- GrapheneOS support
- Signed builds for verified boot (dm-verity/AVB) and re-locking the bootloader with a user-specified key
- Apps: F-Droid (including the privileged extention for automatic installation/updating), Auditor, Backup
- Browser / Webview: Chromium, Bromite, Vanadium
- Seamless OTA updates
- MicroG
- Certain google apps (currently just stuff for Google Fi)
- Easily setting various framework configuration settings such as those found here
- Custom built kernels
- Custom
/etc/hosts
file - Extracting vendor blobs from Google's images using android-prepare-vendor
Future goals include:
- Better documentation, especially for module options
- Continuous integration / testing for various devices
- Automating CTS (Compatibility Test Suite) like nixos tests.
- Automatic verification of build reproducibility
- Replacing android prebuilt toolchains with nixpkgs equivalents.
This has currently only been tested on crosshatch (Pixel 3 XL, my daily driver) and marlin (Pixel XL, which is now deprecated by google and no longer receiving updates).
Here is a single command to build an img
which can be flashed onto a device.
$ nix-build "https://github.com/danielfullmer/robotnix/archive/master.tar.gz" \
--arg configuration '{ device="crosshatch"; flavor="vanilla"; }' \
-A img
The command above will build an image signed with test-keys
, so definitely don't use this for anything intended to be secure.
To flash the result to your device, run fastboot update -w <img.zip>
.
A typical build for robotnix
requires approximately 40GB free disk space to check out the for android source, plus an additional 14GB for chromium.
The AOSP project requires at least 16GB RAM to build.
Ensure your /tmp
is not mounted using tmpfs
, since the AOSP intermediate builds products are very large and will easily use all of your RAM (even if you have 32GB)!
A user can use the --cores
option for nix-build
to set the number of cores to
use, which can also be useful to decrease parallelism in case memory usage of
certain build steps is too large.
A full Android 10 build takes about 4 hours on my quad-core i7-3770 with 16GB of memory.
The included vanilla
and grapheneos
flavors also build chromium
(or vanadium
) from source for use as the system webview.
This takes approximately 6 hours on my i7-3770.
I have recently upgraded to a 3970x Threadripper with 32-cores.
This can build chromium+android in under an hour.
A configuration file should be created for anything more complicated, including creating signed builds.
See my own configuration under example.nix
for inspiration.
After creating a configuration file, generate keys for your device:
$ nix-build ./default.nix --arg configuration ./crosshatch.nix -A generateKeysScript -o generate-keys
$ mkdir keys/crosshatch
$ cd keys/crosshatch
$ ../../generate-keys "/CN=Robotnix" # Use appropriate x509 cert fields
$ cd ../..
Next, build and sign your release. There are two ways to do this. The first option involves creating a "release script" which does the final build steps of signing target files and creating ota/img files outside of nix:
$ nix-build ./default.nix --arg configuration ./crosshatch.nix -A releaseScript -o release
$ ./release ./keys/crosshatch
One advantage of using a release script as above is that the build can take place on a different machine than the signing.
nix-copy-closure
could be used to transfer this script and its dependencies to another computer to finish the release.
The other option is to build the final products entirely inside nix instead of using releaseScript
$ nix-build ./default.nix --arg configuration ./crosshatch.nix -A img --option extra-sandbox-paths /keys=$(pwd)/keys
This, however, will require a nix sandbox exception so the secret keys are available to the build scripts.
To use extra-sandbox-paths
, the user must be a trusted-user
in nix.conf
.
All devices (Pixel 1-4(a) (XL)) have very basic checks to ensure that the android build process will at least start properly.
See release.nix
for the set of configurations with this minimal build testing.
This check is run using nix-build ./release.nix -A check
.
As each build takes approximately 4 hours--I only build marlin and crosshatch builds for myself.
At some point, I would love to set up a build farm and publish build products on s3 or cachix.
This would allow an end-user to simply sign their own releases without building the entire AOSP themselves.
As of Android 10, target-files
, img
, and ota
files all seem to be built reproducibly.
Automated testing of this is still desired.
There are a few places where user-specific public keys are included into the build for key pinning.
This unfortunately decreases the possibility of sharing build products between users.
The F-Droid privileged extension and Trichrome (disabled for now) are two components which have this issue.
Fixes for this are still under investigation.
To build and run an emulator with an attached vanilla system image, use (for example):
$ nix-build ./default.nix --arg configuration '{device="x86"; flavor="vanilla";}' -A build.emulator
$ ./result
Robotnix supports two alternative approaches for fetching source files:
- Build-time source fetching with
pkgs.fetchgit
. This is the default. An end user wanting to fetch sources not already included inrobotnix
would need to create a repo json file usingmk-repo-file.py
and setsource.dirs = lib.importJSON ./example.json;
- Evaluation-time source fetching with
builtins.fetchGit
. This is more convenient for development when changing branches, as it allows use of a shared git cache. The end user will need to setsource.manifest.{url,rev,sha256}
and enablesource.evalTimeFetching
. However, withbuiltins.fetchGit
, thedrv
s themselves depend on the source, andnix-copy-closure
of even just the.drv
files would require downloading the source as well.
Optional CCACHE stuff. As root:
# mkdir -p -m0770 /var/cache/ccache
# chown root:nixbld /var/cache/ccache
Set ccache.enable = true
in configuration, and be sure to pass /var/cache/ccache
as a sandbox exception when building.
See also: NixDroid, RattlesnakeOS, aosp-build, and CalyxOS