Introduction: Unlocking Peak Performance in Android Virtualization
Running Android on QEMU-based virtual machines (VMs) like those powering Anbox, Waydroid, or even custom Android Studio emulators often presents a significant challenge: performance. Out-of-the-box Android kernels are designed for a broad range of physical hardware, leading to inefficiencies when virtualized. They carry extensive drivers and configurations unnecessary for a virtualized environment, introducing overhead and impacting responsiveness. This article provides an expert-level guide to optimizing your AOSP (Android Open Source Project) kernel specifically for QEMU, leveraging paravirtualization technologies and fine-tuning configurations to achieve near-native performance.
By compiling a custom kernel, you can strip away extraneous code, enable critical virtio drivers, and configure CPU optimizations tailored for the host machine and QEMU’s virtualization capabilities. This deep dive will walk you through the entire process, from setting up your build environment to running your optimized Android VM.
Prerequisites and Environment Setup
Setting up the AOSP Build Environment
Before you can compile an AOSP kernel, you need a robust Linux development environment. A modern Ubuntu LTS release (e.g., 20.04 or 22.04) is recommended. Ensure you have sufficient disk space (200GB+ for AOSP sources) and at least 16GB of RAM.
Install essential build tools:
sudo apt update && sudo apt upgrade -y
sudo apt install -y git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev libgl1-mesa-dev libxml2-utils xsltproc fontconfig imagemagick openjdk-11-jdk bc ccache libssl-dev
Configure `repo`, the tool used to manage AOSP Git repositories:
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
Acquiring and Configuring the AOSP Kernel Source
Cloning the Kernel Repository
First, create a directory for your AOSP kernel project and initialize the `repo` client. For QEMU, we often use the `common` kernel tree or a `qemu` specific one if available and maintained. Let’s target `android-13.0.0_r0.6` as an example branch, which corresponds to the Android 13 release:
mkdir aosp-kernel-qemu
cd aosp-kernel-qemu
repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r0.6 --depth=1
repo sync platform/prebuilts/clang platform/prebuilts/gcc platform/prebuilts/build-tools
Now, clone the kernel common repository. Note that the exact kernel source might depend on the Android version and QEMU target. For most virtualized scenarios, `kernel/common` is a good starting point, possibly with specific `qemu` patches if they exist in `kernel/qemu`.
git clone https://android.googlesource.com/kernel/common common-kernel
cd common-kernel
git checkout android-13
Initial Kernel Configuration
The kernel compilation process requires setting up architecture and cross-compilation toolchains. AOSP provides prebuilt toolchains in `platform/prebuilts`. Adjust `ARCH` and `CROSS_COMPILE` based on your target (e.g., `arm64` for modern Android VMs, `x86_64` for x86 targets).
export KERNEL_DIR=$(pwd)
export ARCH=arm64
export KBUILD_OUTPUT=$KERNEL_DIR/out
export PATH=$(realpath ../platform/prebuilts/clang/host/linux-x86/clang-r450784d/bin):$(realpath ../platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin):$PATH
export CROSS_COMPILE=aarch64-linux-android-
export CROSS_COMPILE_ARM64=aarch64-linux-android-
mkdir -p $KBUILD_OUTPUT
# Start with a suitable base configuration for generic Android or QEMU
make O=$KBUILD_OUTPUT gki_defconfig
The `gki_defconfig` is a generic kernel image configuration. For QEMU, we need to customize it further.
Deep Dive into Kernel Configuration with menuconfig
This is the most critical step. Run `make menuconfig` to interactively configure the kernel. Navigate through the menus and make the following changes:
make O=$KBUILD_OUTPUT menuconfig
General Setup:
- Ensure `Local version – append to kernel release` is set (e.g., `-qemu-optimized`) for easy identification.
Processor type and features:
- Paravirtualized guest support: Enable `KVM paravirtualized clock` and other KVM-related options if present. This is crucial for performance.
- Disable specific CPU families or features not relevant to the virtual CPU QEMU provides. Enable optimizations for your host CPU architecture if cross-compiling for the same architecture (e.g., `ARMv8.2-A` if your host is a modern ARM server).
- Consider increasing `Timer frequency` to 1000 Hz (`CONFIG_HZ_1000`) for better responsiveness in VMs.
Device Drivers:
- Virtio drivers (critical for performance):
- `Block devices -> Virtio block driver` (for virtual disk access)
- `Network device support -> Virtio network driver` (for virtual network interface)
- `Graphics support -> Virtio GPU driver` (for accelerated graphics; requires `virtio-gpu` device in QEMU)
- `Input device support -> Virtio input driver` (for virtual input devices)
- `SCSI device support -> Virtio SCSI driver` (alternative to virtio-block for some setups)
- Disable unnecessary hardware support: Go through `SCSI device support`, `ATA/ATAPI/MFM/RLL support`, `MMC/SD/SDIO card support`, `USB support`, `Sound card support`, `Graphics support` (beyond virtio-gpu), `Network device support` (beyond virtio-net) and disable any drivers for physical hardware that will not be present in QEMU (e.g., specific WLAN chips, Bluetooth, camera sensors, specific USB controllers, etc.). This significantly reduces kernel size and boot time.
- File systems: Keep `ext4`, `tmpfs`, `procfs`, `sysfs` and other necessary Android filesystems. You might disable less common ones.
Kernel Hacking:
- Disable debugging features like `Debug Filesystem`, `KGDB`, `Latency Top` unless you specifically need them for kernel development.
After making changes, save the configuration to `.config` in your `KBUILD_OUTPUT` directory.
Compiling Your Custom Kernel
With the configuration saved, you can now compile your custom kernel. The `Image` file is the kernel binary, and `modules.ko` (if any) are the kernel modules.
make -j$(nproc) O=$KBUILD_OUTPUT
make -j$(nproc) O=$KBUILD_OUTPUT modules
The `-j$(nproc)` flag tells `make` to use all available CPU cores for faster compilation. The compiled kernel image will be located at `$KBUILD_OUTPUT/arch/arm64/boot/Image` (or `Image.gz` / `bzImage` for x86).
Integrating and Running with QEMU
Using the Kernel with an AOSP Build
If you’re building a full AOSP image, you can replace the default QEMU kernel with your custom one. Copy your compiled `Image` file to the appropriate location within your AOSP tree:
cp $KBUILD_OUTPUT/arch/arm64/boot/Image /path/to/your/aosp_root/prebuilts/qemu-kernel/arm64/Image.gz-vfe
The exact path may vary based on your AOSP version and target. Then, rebuild your AOSP images:
cd /path/to/your/aosp_root
source build/envsetup.sh
lunch aosp_arm64-eng # Or your specific target
make -j$(nproc)
This will create `system.img`, `userdata.img`, `ramdisk.img`, etc., which you can then boot with QEMU.
Running the Custom Kernel Directly with QEMU
You can also use your custom kernel directly with an existing Android root filesystem and ramdisk. This is useful for testing without a full AOSP build.
First, ensure you have an `initrd` (ramdisk.img) and an Android system image (e.g., from a prebuilt AOSP emulator, Anbox, or Waydroid). For example, if you’ve extracted a Waydroid `system.img` and `vendor.img`, you’d use a command similar to this:
qemu-system-aarch64
-M virt -cpu host -smp 4 -m 4096M -enable-kvm
-kernel $KBUILD_OUTPUT/arch/arm64/boot/Image
-initrd /path/to/your/ramdisk.img
-append
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 →