Android Emulator Development, Anbox, & Waydroid

Modifying AOSP QEMU System Images: Customizing Android Kernels and Ramdisks for Emulation

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to AOSP QEMU Customization

The Android Open Source Project (AOSP) provides a powerful and flexible platform for developing and testing Android. When combined with QEMU, the open-source emulator, it offers an invaluable environment for deep-level system exploration, security research, driver development, and custom ROM creation. While the Android SDK emulator images are convenient, they often lack the granular control needed for advanced scenarios. This guide delves into the core architecture of AOSP QEMU, demonstrating how to extract, modify, and reassemble Android kernels and ramdisks to build truly custom emulation environments.

Understanding and manipulating these low-level components empowers developers to:

  • Test custom kernel modules or drivers.
  • Modify the Android boot process (init.rc scripts).
  • Inject custom binaries or services at boot.
  • Perform security analysis on modified system components.
  • Debug issues that occur very early in the boot sequence.

Understanding AOSP QEMU Architecture

When you launch an AOSP-based QEMU emulator, several key images come into play, forming the complete Android environment. Each serves a distinct purpose:

  • Kernel Image (kernel-qemu or Image): This is the Linux kernel tailored for Android, responsible for managing hardware, processes, and memory. It’s the very first component loaded by QEMU.
  • Ramdisk (ramdisk.img): A small initial RAM filesystem. The kernel mounts this as its root filesystem at boot. It contains the init executable (the first user-space process) and init.rc scripts that orchestrate the remainder of the boot process, including mounting the system.img and vendor.img.
  • System Image (system.img): The read-only partition containing the core Android operating system, frameworks, libraries, and pre-installed applications. This is typically mounted by the init process from the ramdisk.
  • Vendor Image (vendor.img): Contains hardware abstraction layers (HALs) and device-specific binaries, separating vendor-specific code from the generic Android system.
  • Userdata Image (userdata.img): A writable partition for user-specific data, installed applications, and user settings.

Our focus will be on the kernel and ramdisk, as they are fundamental to controlling the very early stages of the Android boot process within QEMU.

Setting Up Your AOSP Build Environment

To follow along effectively, you’ll need access to an AOSP build environment, even if you don’t perform a full AOSP compilation. This provides necessary tools and a structured output directory for images. For a complete AOSP setup, refer to the official Android documentation. Here’s a quick recap of the essential steps:

# Initialize your repo client (if not already done)repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r79# Sync the repository (this can take many hours and hundreds of GBs)repo synccd aosp_root# Set up the build environment and choose a target lunch aosp_cf_x86_64_phone-userdebug

While `repo sync` is extensive, having a working AOSP tree is crucial for obtaining toolchains and sometimes pre-built components.

Extracting QEMU System Images

Whether you’ve built AOSP yourself or are using pre-built images from the SDK, locating the relevant files is the first step. For a locally built AOSP, they reside within the `out` directory:

cd $AOSP_ROOT/out/target/product/generic_x86_64/# You'll typically find:ls kernel* ramdisk.img system.img vendor.img userdata.img

If you’re using SDK emulator images, you can often find them in your Android SDK installation directory, typically under `platforms/android-xx/images/`. Alternatively, you can have the emulator show you its paths:

emulator -avd Pixel_4_API_33 -show-kernel

This command will output the paths to the kernel, ramdisk, and system images being used by the specified AVD.

Modifying the Android Kernel

Customizing the kernel involves obtaining its source, making modifications, and compiling it. For AOSP QEMU, the `kernel-qemu` often refers to a generic x86_64 or arm64 kernel. For more specific device kernels, you’d need the corresponding manifest.

1. Get Kernel Source

# For common Android kernels (e.g., goldfish for emulators)git clone https://android.googlesource.com/kernel/goldfishcd goldfish# Checkout a specific branch matching your AOSP version (e.g., android-goldfish-4.19)git checkout android-goldfish-4.19

2. Configure and Modify

Ensure you have the correct toolchain available (e.g., from `prebuilts/clang/host/linux-x86/clang-rXXXXX/bin`).

# Set ARCH and CROSS_COMPILE environment variablesexport PATH=$AOSP_ROOT/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/bin:$PATHexport ARCH=x86_64 # or arm64export CROSS_COMPILE=x86_64-linux-android- # or aarch64-linux-android-# Generate a default configuration (e.g., goldfish_defconfig for x86_64)make goldfish_defconfig# Open menuconfig to enable/disable features or add debug optionsmake menuconfig

Now, let’s add a simple `printk` message. Edit `init/main.c` (or a relevant file you wish to modify):

// init/main.c (example modification)#include <linux/kernel.h>// ... somewhere in start_kernel() or later in bootprintk(KERN_INFO "MYCUSTOM: Hello from custom Android kernel!n");

3. Compile the Kernel

make -j$(nproc)

Upon successful compilation, your new kernel image will typically be located at `arch/x86_64/boot/bzImage` (for x86_64) or `arch/arm64/boot/Image` (for arm64). This is the `kernel-qemu` replacement.

Customizing the Ramdisk (initrd.img)

The ramdisk is a `cpio` archive containing the initial filesystem. Modifying it is crucial for altering the boot sequence or adding early-stage binaries.

1. Extract the Ramdisk

# Create a working directory and copy the original ramdisk.imgmkdir ramdisk_workcd ramdisk_workcpio -idm < ../ramdisk.img

You’ll now see the contents of the ramdisk, including the crucial `init` executable and the `init.rc` file.

2. Modify the Ramdisk

Let’s add a simple script and ensure it executes during boot by modifying `init.rc`.

# Create a simple script (e.g., in ramdisk_work/myscript.sh)echo '#!/system/bin/sh' > myscript.shecho 'echo "MYCUSTOM: Running custom ramdisk script!" > /dev/kmsg' >> myscript.shecho 'setprop persist.sys.mycustom.ran 1' >> myscript.shechmod 755 myscript.sh# Modify init.rc to execute the scriptvi init.rc

Inside `init.rc`, find a suitable place (e.g., before mounting `/system`) and add:

# init.rc (example modification)on boot    # ... existing commands    exec u:r:su:s0 -- /myscript.sh    # ... existing commands

The `exec` command runs `myscript.sh` as a service. Using `/dev/kmsg` ensures the output goes to the kernel log.

3. Repack the Ramdisk

Navigate back into the `ramdisk_work` directory, then repack the contents into a new `cpio` archive:

cd ramdisk_workfind . | cpio -o -H newc > ../new_ramdisk.img

You now have your `new_ramdisk.img` in the parent directory.

Reassembling and Running the Custom QEMU Image

With your custom kernel and ramdisk ready, it’s time to launch the QEMU emulator using these modified components.

# Assuming your custom kernel is named 'bzImage' and custom ramdisk is 'new_ramdisk.img'emulator -kernel /path/to/your/bzImage          -ramdisk /path/to/your/new_ramdisk.img          -system $AOSP_ROOT/out/target/product/generic_x86_64/system.img          -writable-system          -partition-size 4096          -memory 4096          -qemu -append "console=ttyS0"

The `-writable-system` flag allows modifications to the system image (though we didn’t modify it in this guide, it’s often useful). `-append “console=ttyS0″` directs kernel messages to the standard output, making debugging easier.

Verification

Once the emulator boots, open a shell and check the kernel log for your custom messages:

adb shell dmesg | grep MYCUSTOM

You should see output similar to:

<6>MYCUSTOM: Hello from custom Android kernel!<6>MYCUSTOM: Running custom ramdisk script!

This confirms both your kernel and ramdisk modifications were successful.

Conclusion

Modifying AOSP QEMU system images, particularly the kernel and ramdisk, opens a world of possibilities for Android system developers, security researchers, and enthusiasts. By understanding the boot process and having the tools to inject custom code at crucial stages, you gain unparalleled control over your Android emulation environment. This detailed guide provides the foundation for more complex customizations, enabling deeper insights into the Android operating system’s inner workings.

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 →
Google AdSense Inline Placement - Content Footer banner