AOSP.dev — 3: Building your first Android image (on Ubuntu) _part-2
Note: Depending on the version of Ubuntu you have installed, it might be possible that you are missing some packages and/or Java versions which are essential for the build to succeed. I still suggest downloading all the packages mentioned in this article.
Configuring the build for your target device
Now that the syncing has completed, we can configure our terminal session with the AOSP tools to make sure all the correct parameters and variables are set to commence the build for our target device. We’re going to start this off with a script, properly named envsetup.sh:
$ . build/envsetup.sh
As the name might suggest, this script will help in setting up the AOSP environment. It will load in the scripts of the buildable devices, and enable the use of the commands necessary to specify & build the images for your target device, as well as some grep-like helper commands. Though the latter ones can be useful, I highly suggest just getting acquainted with ag-silversearcher for all your grep-/find-related needs in general. It will change the way you use your shell, I promise.
Choosing a build combination
We can specify different types of builds for our target device, depending on the configuration we select. This configuration is called a build combination. Specifying a build combination is done using the lunch command, and there are several elements you must take into account:
The product is the main argument and will refer to the target device/package. Considering all Nexus/Pixel devices are codenamed (as aquatic animals I believe?), you will first have to determine what the codename is of your target device. This can be done using the factory images list. I use a Nexus 6P for example, and searching in the list we can find that it’s codenamed ‘angler’. So I will have to use aosp_angler as my product.
As far as the ‘aosp_’ prefix goes, I think this is still left from a legacy system in which you could specify the modules/packages you want to build but I believe it’s deprecated now. If you’re doubting which prefix you should use, normally when just running ‘lunch’ in your shell, it should display a list of the possible options with the correct prefixes.
The variant argument refers to the amount of debug tooling & system access the final image will provide. For example, when building a user-type image, it’s like building an app in Release mode. Your image won’t allow root access in the shell, maximal SELinux security will be enabled, … While an eng-type image will provide root access, add additional error logging and checking, etc …
I personally prefer to build userdebug images considering they run a lot smoother than eng-types while still allowing root access on the phone for any necessary debugging.
Specifying the build combination
So let’s say we decide on a userdebug build combination for the Nexus 6P, then we can proceed in two manners:
# Simply running 'lunch' alone will launch a script from which youcombination
# have to select a build
by either entering a number orcombination
# specifying the full build
# Second option is giving the full build combination as an argument
$ lunch aosp_angler-userdebug
Once we completed one of these two options, the system will start configuring and preparing the session for a build.
Building the image
If you’re excited and just want to trigger the build already:
# 'N' is the number discussed at the bottom of the previous article
$ make -jN
If all went well, your terminal should start moving and your CPU should be doing overtime as seen below.
It is very much possible that you experience some build error either related to missing packages on your system, the toolchain crapping itself (restart the build if this happens) or even other reasons. Feel free to add a comment below with a description of your problem considering I might have the solution at hand.
So once the build is triggered by the ‘make’ command, the toolchain will first start going through the whole AOSP directory to include all necessary Android makefiles (usually Android.mk) and it will create a dependency graph to build everything in the right order.
The latter is necessary due to the fact that if an app-module depends on a certain library-module for example, the build would fail if it would start building the app and the library wasn’t built & available already.
There are also commands to build target modules separately, but we will tackle this in a later chapter. While the build is finishing up, read up on the following session to learn more about the build results.
The build results will be placed inside of the out/ directory in the root of the AOSP. Inside of out/, there are 2 main directories: host/ & target/. host/ contains all of the necessities for your host/build system to complete the image build. These are libraries, binaries, …
target/ is the directory that is most interesting to us and contains all of the build output related to the selected device (“angler”/Nexus 6P in this case). Within target/, you have 2 big subdivisions again: common/ & product/. So basically common/ will contain code that should be common across different devices. Whether you build an image for the Nexus 6P or you build an image for the Pixel, a java library or app will be the same on both devices considering it runs in a sandboxed virtual machine.
Within the product/ folder, you will find a directory for the target device containing the finalised build output generated in common/ (.apk, .dex, .odex, .jar, … ; no object files) along with files that are specific for those devices. For example, in product/angler/, you will find (almost) all files that compose the final images for a Nexus 6P device.
Flashing the image
Note: This section can only be guaranteed for Nexus/Pixel devices, and while the tools might work for a lot of other products as well, there are many mobile phones that need proprietary flashing tools such as Odin/Heimdall for Samsung and the SP Flash Tool for MediaTek-based ones. These might be tackled in a later chapter.
There are 2 main tools necessary to flash an image to a phone:
- adb: The android debugging bridge. This is an all-round Swiss pocket knife when dealing with Android development (especially rooting & AOSP).
- fastboot: This tool is used to interact with your device’s flash memory and can be used to flash new images and bootloaders.
Both of these tools come with the Android SDK, but nowadays you can even get them in a separate package for your preferred OS.
Before we start flashing, we should first make sure that your system is able to communicate with the device & bootloader. To do this, implement the necessary udev rules for your system. Instructions can be found here.
After the instructions above have been followed, your device should be able to be detected using ADB. Considering ADB is such a vast tool, I won’t go to deep into it here but feel free to read up on it. The only commands which are relevant for us now, are the ones below:
# Use this to check whether the device has been detected successfully
$ adb devices
# Reboot into bootloader mode
$ adb reboot-bootloader
Once you boot into bootloader mode, you should normally be presented with the following or similar visual:
As the image shows on the right side, your device is (normally) still in locked mode and needs to be unlocked to flash a different ROM. This is where ‘fastboot’ comes into play. To unlock your device, run the following command:
# !NOTE: Running this command will wipe all data from the device as a security precaution
$ fastboot flashing unlock
Running ‘flashing unlock’ will display a menu where you first need to confirm that you want to unlock the device. You have to select “Yes” (Using your volume controls) and press the power button to confirm. Once this is done, run the following command to reboot into bootloader mode again and reinitialise:
$ fastboot reboot-bootloader
After the device rebooted, we can now start flashing the image.
There are several options to flash the generated sparse images onto the device. The best known way is to simply execute:
# !NOTE: This command only works if it's executed within the same session where the 'envsetup' & 'lunch' configuration have been executed as well.
# You can add the -w flag to wipe all data from the phone
$ fastboot flashall
‘flashall’ will use the available environment information from lunch to determine where it can find the appropriate images for the current device and flash them. (Read the following slides to learn more about the different type of images)
Normally when you run this command, your terminal will start moving and showing which images are being flashed to the phone. This process should take a few minutes, and once done your device will be rebooted into normal mode with your images loaded.
- flash <image>
If you only want to flash an individual image (f.e.: system.img) then you can make use of the fastboot flash command. Flashing the system image would work as demonstrated below:
# Flash the system image for an angler device
$ fastboot flash system out/target/product/angler/system.img
# Flash the boot image for an angler device
$ fastboot flash boot out/target/product/angler/boot.img
Aside from building your image with a plain ‘make’ command, you can also build the image using ‘make updatepackage’. This latter command will also create a flashable .zip archive that contains images for system, userdata, recovery, boot,…
To flash the device using the update package, execute following command:
# !NOTE: Despite having the 'eng' tag in the name, this updatepackage can be a result of userdebug/user builds as well
$ fastboot update out/target/product/angler/$TARGET_PRODUCT-img-eng.$USER.zip
If everything went well up to this point, your device should now be running your very own built version of Android. You can double check this in the Settings > About phone menu where some parameters will be unique to your system.
Should you have encountered any errors which you can’t overcome, feel free to ask a question below or join us on the Android Development Discord #rom-dev channel using this link.