Android Emulator Development, Anbox, & Waydroid

Creating a Rooted AVD System Image: Full Control and Deep Customization for Advanced Testing

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Unlocking Android Emulator Potential

Android Virtual Devices (AVDs) are indispensable tools for app development, testing, and security research. While standard AVDs provide a clean slate for typical application testing, advanced scenarios often demand deeper control, such as modifying system files, debugging low-level processes, or bypassing security restrictions. This is where a rooted AVD system image becomes invaluable, offering full root access within the emulator environment.

Creating a custom rooted AVD system image allows developers and researchers to tailor the Android environment precisely to their needs, enabling capabilities beyond what a typical userdebug build provides. This guide will walk you through the process of setting up an Android Open Source Project (AOSP) build environment, integrating root capabilities, building your custom system image, and deploying it to an AVD.

Why Create a Rooted AVD System Image?

  • Advanced Debugging: Inspect and modify system-level components.
  • Security Research: Test exploits, analyze malware behavior in a controlled environment.
  • Custom ROM Development: Prototype and test system-level changes before flashing to physical devices.
  • Deep Customization: Pre-install system apps, modify framework services, alter SELinux policies.

Prerequisites: Preparing Your Build Environment

Before diving into the AOSP build process, ensure your workstation meets the necessary requirements. AOSP builds are resource-intensive, requiring significant disk space and RAM.

  • Operating System: Ubuntu 18.04 LTS or newer (recommended).
  • Disk Space: At least 250GB free for AOSP source and build artifacts.
  • RAM: 16GB or more (32GB+ recommended).
  • Processor: Modern multi-core CPU (Intel i7/i9 or AMD Ryzen 7/9 recommended).
  • Java Development Kit (JDK): Specific versions depending on the Android version you’re building. For Android 11-13, OpenJDK 11 is required.
  • Essential Build Tools: Git, Repo, Python, make, gcc, g++ and various other utilities.

Install the necessary packages:

sudo apt-get update
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 libgl1-mesa-dev libxml2-utils xsltproc fontconfig imagemagick openjdk-11-jdk

Configure Git with your name and email:

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

Setting Up the AOSP Build Environment

1. Initialize and Sync AOSP Source

First, create a working directory for your AOSP source code and initialize the `repo` tool. For this guide, we’ll target a recent stable Android version, e.g., Android 13 (`android-13.0.0_r60`).

mkdir aosp-root-build
cd aosp-root-build

repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r60 --depth=1 # Choose your desired branch
repo sync -j$(nproc)

The `repo sync` command will download the entire AOSP source code, which can take several hours depending on your internet connection.

2. Configure the Build Environment

Once the source is synced, set up the build environment script:

source build/envsetup.sh

Modifying AOSP for Root Access

Standard `user` builds do not allow `adb root` or provide an `su` binary. `userdebug` and `eng` builds typically allow `adb root` directly. To create a system image with persistent root access for applications, we need to embed an `su` binary and adjust its permissions and SELinux policy.

1. Choose a Build Target

For emulator builds, targets like `aosp_x86_64-eng` or `aosp_x86-eng` are ideal as they provide enhanced debugging capabilities and already allow `adb root` by default.

lunch aosp_x86_64-eng

2. Integrate the `su` Binary

We’ll integrate a basic `su` binary. A simple approach involves adding a pre-compiled `su` to your build. For demonstration, we’ll assume you’ve obtained a compatible `su` binary (e.g., from an open-source project like `toybox`’s `su` or a simplified version). Place it in a directory within your AOSP tree, for example, `external/custom_su/su`.

Next, modify the build system to include this binary and set the necessary permissions. You’ll typically edit a product makefile, such as `device/generic/goldfish/emulator.mk` or a custom product overlay, to add your `su` binary to `PRODUCT_PACKAGES` and ensure it’s installed as setuid root.

Create a directory `device/generic/goldfish/custom_root/` and inside, create `custom_root.mk`:

# device/generic/goldfish/custom_root/custom_root.mk

PRODUCT_PACKAGES += 
    su

# Add the su binary to the system image
PRODUCT_COPY_FILES += 
    external/custom_su/su:system/bin/su

# Ensure su has root ownership and setuid permissions
PRODUCT_PROPERTY_OVERRIDES += 
    ro.boot.root_access=1

# Modify init.rc to set permissions for su
PRODUCT_ARTIFACT_PATH_REQUIREMENTS := 
    $(filter-out system/bin/su,$(PRODUCT_ARTIFACT_PATH_REQUIREMENTS))

Then, modify `device/generic/goldfish/emulator.mk` to include your `custom_root.mk`:

# device/generic/goldfish/emulator.mk

# ... existing content ...

# Include custom root modifications
$(call inherit-product, device/generic/goldfish/custom_root/custom_root.mk)

You will also need to adjust SELinux policy to allow applications to execute `su` and for `su` to transition to the root domain. This is a complex step requiring careful modification of `system/sepolicy` files (e.g., `file_contexts`, `domain.te`, `su.te`). For a basic rooted emulator, you can temporarily disable SELinux enforcement for testing purposes by adding `androidboot.selinux=permissive` to the kernel command line, or by issuing `setenforce 0` via `adb shell` once the AVD boots (though this is not persistent without further modifications).

For a persistent solution, you would typically add something like this to `system/sepolicy/public/domain.te` (simplified):

# system/sepolicy/public/domain.te (simplified example)
allow appdomain su_exec:file { execute execute_no_trans read entrypoint };

# For su itself
type su_exec, exec_type, file_type, system_file_type;
type su, domain;

init_daemon_domain(su) # If su is started by init

# Allow su to execute other programs as root
allow su self:capability { setuid setgid net_raw net_admin dac_override fowner fsetid audit_write };
allow su domain:fd use;
allow su domain:{ process transition siginh rlimit noatsecure };
allow su { domain -su }:process { transition };
allow su { devpts tty }:{ chr_file rw_file_perms };
allow su self:process { setcap setcurrent };
# ... more specific rules ...

And add to `system/sepolicy/private/file_contexts`:

# system/sepolicy/private/file_contexts
/system/bin/su u:object_r:su_exec:s0

These SELinux modifications are critical for a properly functioning `su` binary under modern Android. Without them, even if `su` is present, SELinux will prevent its execution or domain transition.

Building the Custom System Image

With the modifications in place, you can now build your custom AVD system image. This step will compile all source code and package it into the necessary image files.

make -j$(nproc)

This command will build the entire AOSP. The `$(nproc)` part tells `make` to use all available CPU cores, significantly speeding up the compilation. This process can take several hours, even on powerful machines.

Creating an AVD with the Custom Image

Once the build completes successfully, your custom image files (e.g., `system.img`, `ramdisk.img`, `userdata.img`, `kernel`) will be located in `out/target/product/goldfish_x86_64/` (or your chosen target directory).

1. Locate Image Files

Navigate to your build output directory:

ls out/target/product/goldfish_x86_64/

You should see `system.img`, `ramdisk.img`, `userdata.img`, and `kernel-qemu-x86_64`.

2. Create a New AVD

You can create an AVD using Android Studio’s AVD Manager or via the command line using `avdmanager` from the Android SDK.

First, ensure your `PATH` includes the Android SDK `tools` and `platform-tools` directories. For example:

export PATH="$PATH:/path/to/android-sdk/emulator"
export PATH="$PATH:/path/to/android-sdk/platform-tools"
export PATH="$PATH:/path/to/android-sdk/tools/bin"

Create a system image package for your custom build. While you can manually specify image paths when launching QEMU, integrating it with `sdkmanager` makes AVD creation easier. For simplicity, we’ll demonstrate direct QEMU launch, or using `avdmanager` by pointing it to a custom system image directory.

The simplest way to use your custom images is often to copy them into the SDK’s system image directory and then create an AVD referencing that location, or directly launching with QEMU parameters.

Option A: Direct Launch (for quick testing)

emulator -avd YOUR_AVD_NAME -system /path/to/aosp-root-build/out/target/product/goldfish_x86_64/system.img -ramdisk /path/to/aosp-root-build/out/target/product/goldfish_x86_64/ramdisk.img -kernel /path/to/aosp-root-build/out/target/product/goldfish_x86_64/kernel-qemu-x86_64 -writable-system

Option B: Using `avdmanager` (more integrated)

Copy your custom images into a structure that `sdkmanager` can recognize. For example:

mkdir -p ~/.android/avd_custom/system-images/android-33/default/x86_64/
cp out/target/product/goldfish_x86_64/system.img ~/.android/avd_custom/system-images/android-33/default/x86_64/
cp out/target/product/goldfish_x86_64/ramdisk.img ~/.android/avd_custom/system-images/android-33/default/x86_64/
cp out/target/product/goldfish_x86_64/userdata.img ~/.android/avd_custom/system-images/android-33/default/x86_64/
cp out/target/product/goldfish_x86_64/kernel-qemu-x86_64 ~/.android/avd_custom/system-images/android-33/default/x86_64/kernel

avdmanager create avd -n MyRootedAVD -k "system-images;android-33;default;x86_64" -d "pixel_3a"

Then, launch your AVD via Android Studio or `emulator -avd MyRootedAVD`.

Verifying Root Access

Once your custom AVD has booted, you can verify root access:

  • Open a terminal and ensure your AVD is listed: `adb devices`
  • Connect to the shell: `adb shell`
  • Attempt to gain root privileges: `su`
  • Verify the user ID: `id` (should show `uid=0(root) gid=0(root)`)

If you see `uid=0(root)`, congratulations! You have successfully created a rooted AVD system image.

Advanced Customization and Further Steps

With a rooted AVD, your possibilities expand significantly:

  • Pre-install Applications: Place APKs in `product/app`, `product/priv-app`, or `system/app` within your AOSP source.
  • Modify Framework Services: Edit services in `frameworks/base` and rebuild.
  • Fine-tune SELinux: Develop specific SELinux policies for new daemons or applications to enhance security while maintaining functionality.
  • Custom Kernel Modules: Build and load custom kernel modules for deep system interaction.

Conclusion

Creating a rooted AVD system image from AOSP provides an unparalleled level of control for Android development, security research, and advanced testing. While the process involves a significant initial setup and understanding of the Android build system and SELinux, the capabilities unlocked are well worth the effort. By following this guide, you’ve gained the knowledge to build a powerful, customized Android emulation environment, opening doors to advanced system-level exploration and innovation.

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