AOSP Foundations
5 min read

Product Makefiles

Learn how Product Makefiles define the software configuration and included apps for a specific Android device.

The 100GB Boot Failure

Imagine pressing a button and building every piece of software inside the Android Open Source Project. You would compile a television interface, an automotive head unit, a smartwatch UI, and a phone dialer simultaneously. Trying to pack an x86 kernel alongside an ARM display driver into a single storage partition guarantees failure. Such a chaotic process would generate an unbootable, bloated operating system easily exceeding 100 gigabytes. Compilation engines need a strict filter to narrow down this massive repository to exactly what your specific hardware requires.

AOSP operates like a massive wholesale warehouse. Shoppers do not try to buy the entire inventory. You bring a highly specific shopping list to get exactly what you need.

Tip: Any code you write that lacks a Makefile reference is essentially dead code to the build system. It will never be compiled.

To prevent chaos, Android relies on a specific hierarchy of configuration files. Builders use these declarations to understand exactly what belongs on the final device image.

The Device Configuration Hierarchy

Android separates a device identity into hardware foundations and software layers. You need a place to define the silicon architecture and a separate place to declare the default browser. Mixing these concerns creates configuration nightmares across different product lines. Platform engineers solve this by dividing the device definition across three distinct Makefiles.

BoardConfig.mk acts as the blueprint of the house. It defines the foundation size, electrical wiring, and the processor architecture. device.mk serves as the interior decoration, dictating which furniture goes into which room. AndroidProducts.mk simply registers the hardware name so the lunch command can find it.

To visualize how the build system reads these files, we can map their inclusion hierarchy. Our flowchart illustrates the specific order in which the lunch command discovers and loads your configuration.

Executing the command triggers the product registry, which then points to the software configuration. That file ultimately pulls in the core hardware blueprint.

Warning: Do not put software dependencies in BoardConfig.mk or hardware partition sizes in device.mk.

With the hierarchy clear, let us look at the most important variable inside device.mk for software developers.

The Master Shopping List

Imagine writing a brand new system application called MyCustomSettingsApp. You created an Android.bp file and defined the module perfectly. Running a full build and flashing the device reveals that the app is completely missing.

Defining a module does not guarantee its inclusion. Compilers need explicit permission to pack your compiled binary into the system image. This mechanism is controlled entirely by the PRODUCT_PACKAGES variable.

This variable acts as the master shopping list for the build system. Appending a module name to this list directly instructs the compilation engine to process that target. Ninja will then compile the code, resolve its dependencies, and copy the final output into the /system or /vendor partition.

PRODUCT_PACKAGES += \
    MyCustomSettingsApp \
    ChromeBrowser \
    CustomBluetoothDriver

Common Mistake: The PRODUCT_PACKAGES variable requires the exact module name defined in your Android.bp file. Never use the file path or the .apk file name.

Including apps handles only half the job of device.mk. The other half provides the device its public facing identity.

Branding the Silicon

Every commercial device needs a public identity. Applications need to know if they are running on a Pixel phone or a generic test board. The Settings app displays a human readable name to the user.

These identities are not hardcoded into Java classes. Build processes generate them dynamically from variables defined in your device.mk file. You define a brand, a device name, and a user facing model string.

PRODUCT_NAME := aosp_coral
PRODUCT_DEVICE := coral
PRODUCT_BRAND := google
PRODUCT_MODEL := Pixel 4 XL

These variables get baked directly into the device properties file during compilation. When the device boots, the Android property service loads them into memory. You can verify this mapping on any live device using the Android Debug Bridge.

adb shell getprop ro.product.model

This command queries the running property service and outputs Pixel 4 XL. Engineers often pull their hair out trying to find where a specific property is set. Searching the product Makefile usually reveals the source of truth.

Interview Note: Always differentiate PRODUCT_NAME, which is the internal build target used by the lunch command, from PRODUCT_MODEL, which is the user facing property.

The scale of AOSP requires strict filtering to produce a bootable image. Separating hardware rules into BoardConfig.mk and software selections into device.mk solves this configuration challenge. Using the PRODUCT_PACKAGES variable creates the ultimate bridge between source code modules and the final compiled partitions. Runtime system properties trace their origin directly back to these Makefile declarations.

We have the blueprint and the shopping list. Next, we meet the engine that actually reads these files and constructs the operating system from scratch.