Introduction: The Challenge of AOSP on IoT
The Android Open Source Project (AOSP) is a powerful, versatile operating system, but its default configuration is tailored for feature-rich smartphones and tablets. Running a full AOSP build on resource-constrained, low-power ARM IoT gateways presents significant challenges regarding memory, storage, CPU cycles, and power consumption. These embedded devices often operate in headless or minimal UI modes, performing specific tasks like data aggregation, edge processing, or protocol translation, making many of AOSP’s standard components unnecessary bloat.
This expert-level guide delves into the methodologies for optimizing and slimming down AOSP, transforming it into a lean, efficient operating system suitable for custom ARM-based IoT hardware. We will cover build configuration, kernel customization, storage optimization, and runtime tweaks to achieve a performant and power-efficient Android instance.
Understanding AOSP’s Default Footprint
A stock AOSP build typically includes a wide array of services, frameworks, and applications: a full graphical user interface (Launcher, SystemUI), media codecs, a comprehensive set of Google applications (if building with GMS), accessibility services, and various hardware abstraction layers (HALs) that may not be relevant to your specific IoT device’s peripherals. This leads to:
- Large Storage Footprint: Images often exceed several gigabytes.
- High RAM Consumption: Many services run persistently in the background.
- Increased CPU Load: Background processes can consume valuable cycles.
- Higher Power Draw: Unnecessary components contribute to overall energy usage.
For an ARM IoT gateway, which might feature as little as 512MB RAM, 4GB eMMC storage, and a single-core ARM Cortex-A7 processor, these default configurations are simply unsustainable.
Key Optimization Strategies
1. Build Configuration: The Foundation of Leanness
The most impactful optimizations begin at the AOSP build system level. This involves carefully selecting target products, customizing build flags, and pruning unnecessary modules.
Choosing the Right Target
Instead of `aosp_arm` or `aosp_arm64`, consider more minimal targets if available for your architecture, or start with `aosp_base` and build up. For many IoT scenarios, a headless or minimal UI is sufficient.
lunch aosp_arm-eng
This command selects the `aosp_arm` product. We need to go deeper.
Customizing Product Packages
The heart of trimming lies within your device’s product definition files, typically located under `device///`. You’ll primarily work with `device.mk` and `BoardConfig.mk`.
First, identify what can be removed. Common candidates include:
- Standard UI applications: Launcher, Gallery, Calculator, Calendar, Email, Music, Camera, Browser.
- Accessibility services.
- Specific media codecs or frameworks if your device doesn’t require rich multimedia.
- Unused Input Method Editors (IMEs).
- Debug tools (for production builds).
In `device///device.mk`, you’ll find the `PRODUCT_PACKAGES` variable. Comment out or remove entries for components you don’t need. For instance, to remove common UI apps:
# Remove standard Android UI applications to save space and resources. #PRODUCT_PACKAGES +=
# Launcher3
# Gallery2
# Calculator
# Email
# Camera2
# Browser
# QuickSearchBox
# Mms
# TeleService
# Settings
# SystemUI
# SettingsProvider
# Bluetooth
# CalendarProvider
# ContactsProvider
# CallLogBackup
# HTMLViewer
# PacProcessor
# PrintSpooler
# LiveWallpapers
# LiveWallpapersPicker
# KeyChain
# WallpaperCropper
# WallpaperPicker
# LegacyCamera
# SoundRecorder
Alternatively, if you’re building a truly minimal system, you can start with a very limited set of packages and add only what’s essential:
PRODUCT_PACKAGES :=
framework-res
services
libandroid_runtime
libbinder
libcamera_client
libEGL
libGLESv1_CM
libGLESv2
libui
Toybox
toolbox
adbd
logd
debuggerd
installd
healthd
lmkd
app_process32
dex2oat
am
pm
surfaceflinger
bootanimation
minigzip
sdcard
vold
storaged
zygote32
update_engine_sideload
update_engine
recovery
charger_res_images
init
init.recovery
init.usb.configfs.rc
init.usb.rc
ueventd
console
wpa_supplicant.conf
fs_mgr
fs_mgr_ramdisk
hostapd
netd
This example dramatically reduces the build to core system components, a shell, and essential utilities, suitable for headless operations.
Disabling Features in `BoardConfig.mk`
In `BoardConfig.mk`, you can disable entire subsystems:
# Disable graphics components if running headless
BOARD_HAS_NO_GRAPHICS := true
# Disable audio if not needed
BOARD_USES_ALSA_AUDIO := false
# Disable Bluetooth/Wi-Fi if not used
BOARD_HAVE_BLUETOOTH := false
BOARD_WPA_SUPPLICANT_DRIVER := NONE
BOARD_WLAN_DEVICE := NONE
# Reduce ART/Dalvik heap sizes for low-memory devices
PRODUCT_PROPERTY_OVERRIDES +=
dalvik.vm.heapgrowthlimit=64m
dalvik.vm.heapsize=256m
2. Kernel Customization
The Linux kernel that powers Android also contributes to the system’s footprint and resource usage. Building a custom kernel involves:
- Disabling Unused Drivers: Remove drivers for peripherals your IoT device doesn’t have (e.g., NFC, specific sensors, display controllers if headless).
- Minimal Configuration: Use `make menuconfig` or `make xconfig` in your kernel source directory to strip down features. Pay attention to networking options, filesystem support, and debugging features.
- Memory Management: Fine-tune kernel memory parameters for efficiency in low-RAM environments.
Compile your custom kernel and ensure your `BoardConfig.mk` points to your `TARGET_PREBUILT_KERNEL` or `TARGET_KERNEL_CONFIG` appropriately.
3. Storage Optimization
Even with fewer packages, AOSP images can be large. Consider these storage optimizations:
- Filesystem Choices: For read-only partitions (like `/system`), consider `squashfs` which offers excellent compression. For read-write partitions (like `/data`), `f2fs` (Flash-Friendly File System) is optimized for NAND flash storage and offers good performance with less wear.
- Partition Sizing: Carefully calculate and reduce the size of `/system`, `/vendor`, and `/data` partitions in your device’s `BoardConfig.mk` or `BoardConfig.mk`’s referenced partition tables.
4. Runtime Optimization
Even after a slim build, some services might start unnecessarily. Use `init.rc` and `adb` to manage runtime behavior.
- Disable Unneeded Services: Modify your device’s `init.rc` files (`device///init.rc` or `system/core/rootdir/init.rc`) to disable services that aren’t critical. Look for `service` entries and consider removing them or changing their `class` to prevent auto-starting.
- Memory Tuning: Android’s Low Memory Killer Daemon (LMKD) helps manage memory. You might need to adjust its thresholds via kernel parameters or Android system properties to be more aggressive on low-RAM devices.
Step-by-Step Guide: Building a Custom Slim AOSP
1. Set Up Build Environment & Download AOSP
Ensure you have a Linux-based build machine (Ubuntu LTS recommended) with sufficient disk space (250GB+) and RAM (16GB+). Install necessary dependencies:
sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc bc rsync android-sdk-platform-tools-common
Initialize and sync the AOSP source code (e.g., Android 11):
mkdir aosp-iot
cd aosp-iot
repo init -u https://android.googlesource.com/platform/manifest -b android-11.0.0_r49
repo sync -j8
2. Customize Your Device Configuration
Let’s assume you’re targeting a fictional ARM board called `iot_gateway` from `myvendor`. Create your device tree:
mkdir -p device/myvendor/iot_gateway
cp -r device/generic/goldfish/device.mk device/myvendor/iot_gateway/
cp -r device/generic/goldfish/BoardConfig.mk device/myvendor/iot_gateway/
cp -r device/generic/goldfish/AndroidProducts.mk device/myvendor/iot_gateway/
cp -r device/generic/goldfish/vendorsetup.sh device/myvendor/iot_gateway/
# Edit device/myvendor/iot_gateway/vendorsetup.sh
# Add the following line:
# add_lunch_combo iot_gateway-userdebug
Now, modify `device/myvendor/iot_gateway/device.mk`. Start by inheriting from a minimal base and then customize:
# Inherit from a minimal Android base
$(call inherit-product, $(SRC_TARGET_DIR)/product/tiny_base.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
# Product characteristics for a headless IoT device
PRODUCT_CHARACTERISTICS := iot
PRODUCT_NAME := iot_gateway
PRODUCT_DEVICE := iot_gateway
PRODUCT_BRAND := MyVendor
PRODUCT_MANUFACTURER := MyVendor
# Override system properties
PRODUCT_PROPERTY_OVERRIDES +=
ro.expect.recovery_id=0
ro.sf.lcd_density=160
dalvik.vm.heapgrowthlimit=64m
dalvik.vm.heapsize=256m
persist.sys.timezone=Europe/Berlin
# Remove unwanted packages
PRODUCT_PACKAGES :=
framework-res
services
libandroid_runtime
libbinder
libui
Toybox
toolbox
adbd
logd
debuggerd
installd
healthd
lmkd
app_process32
dex2oat
am
pm
surfaceflinger
bootanimation
minigzip
sdcard
vold
storaged
zygote32
update_engine_sideload
update_engine
recovery
charger_res_images
init
init.recovery
init.usb.configfs.rc
init.usb.rc
ueventd
console
wpa_supplicant.conf
fs_mgr
fs_mgr_ramdisk
hostapd
netd
# Add specific packages required for your IoT application
# PRODUCT_PACKAGES +=
# MyIoTService
# MyIoTApp
Next, edit `device/myvendor/iot_gateway/BoardConfig.mk` to align with your hardware and desired minimal features:
# Inherit common BoardConfig for ARM
include $(SRC_TARGET_DIR)/board/generic_arm.mk
# Architecture
TARGET_ARCH := arm
TARGET_ARCH_VARIANT := armv7-a
TARGET_CPU_ABI := armeabi-v7a
TARGET_CPU_ABI2 := armeabi
TARGET_CPU_VARIANT := cortex-a7
# Filesystem types for smaller images
TARGET_USERIMAGES_USE_EXT4 := true
BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := f2fs
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
# Kernel configuration (if you have a custom kernel)
TARGET_KERNEL_ARCH := arm
TARGET_KERNEL_SOURCE := kernel/myvendor/iot_gateway
TARGET_KERNEL_CONFIG := iot_gateway_defconfig
# Memory sizes for partitions (adjust based on your actual storage)
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 768000000 # 768MB
BOARD_VENDORIMAGE_PARTITION_SIZE := 256000000 # 256MB
BOARD_USERDATAIMAGE_PARTITION_SIZE := 2048000000 # 2GB
BOARD_CACHEIMAGE_PARTITION_SIZE := 128000000 # 128MB
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 64000000 # 64MB
# Disable unnecessary hardware features
BOARD_HAS_NO_GRAPHICS := true
BOARD_USES_ALSA_AUDIO := false
BOARD_HAVE_BLUETOOTH := false
BOARD_WPA_SUPPLICANT_DRIVER := NONE
BOARD_WLAN_DEVICE := NONE
# Disable some debug features for a production-like build
PRODUCT_DEFAULT_PROPERTY_OVERRIDES +=
ro.debuggable=0
ro.secure=1
3. Build AOSP
Source the build environment and select your custom lunch target:
source build/envsetup.sh
lunch iot_gateway-userdebug
Now, start the build. This can take several hours depending on your hardware:
make -j$(nproc)
Upon successful completion, your optimized images (`system.img`, `boot.img`, `userdata.img`, etc.) will be located in `out/target/product/iot_gateway/`.
4. Flashing to Your Device
The method for flashing images will depend on your specific ARM IoT gateway’s bootloader. Commonly, `fastboot` is used:
adb reboot bootloader
fastboot flash boot out/target/product/iot_gateway/boot.img
fastboot flash system out/target/product/iot_gateway/system.img
fastboot flash vendor out/target/product/iot_gateway/vendor.img
fastboot flash userdata out/target/product/iot_gateway/userdata.img
fastboot reboot
For devices without fastboot support, you might need to use a custom flashing utility provided by the SoC vendor or write images directly to eMMC/SD card via tools like `dd` from a recovery environment.
5. Post-Installation Runtime Tweaks
Even a slim build might have services you want to manage at runtime. You can use `adb` to stop or disable services temporarily or permanently modify `init.rc` files directly on the device (if you have root access and a writable system partition).
To stop a service via `adb`:
adb shell
su
stop [service_name]
For example, to stop the `logd` (logging daemon) if not needed for production logs:
stop logd
To make these changes persistent, you would typically edit the relevant `init.rc` files in your device tree *before* building, as mentioned in the
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →