Chapter 58: Emulator Architecture¶
The Android Emulator is one of the most critical developer tools in the AOSP ecosystem. Far more than a simple simulator, it is a full system-level virtual machine that runs production Android system images inside a modified QEMU hypervisor, with hardware acceleration via KVM on Linux and HAXM/Hypervisor Framework on other platforms. This chapter dissects the emulator from the inside out -- the QEMU execution engine, the Goldfish and Ranchu virtual hardware platforms, the guest-side HAL implementations that bridge virtual devices to the emulator host, the Cuttlefish cloud-oriented alternative, and the rich set of developer-facing features (snapshots, multi-display, foldable simulation) that make the emulator indispensable.
The device tree that underpins the emulator lives under
device/generic/goldfish/ in the AOSP source. A second virtual device
platform, Cuttlefish, lives under device/google/cuttlefish/. Together these
two directories contain hundreds of thousands of lines of C++, shell scripts,
SELinux policy, and Makefile configuration that define what "an Android device"
means when there is no physical hardware.
58.1 Emulator Architecture Overview¶
58.1.1 The Software Stack¶
The Android Emulator is built on a custom fork of QEMU, the open-source
machine emulator and virtualizer. When a developer types emulator at the
command line, the following layered architecture comes into play:
Host machine (Linux/macOS/Windows)
|
+-- Android Emulator binary (emulator, qemu-system-*)
|
+-- QEMU core (TCG for software emulation, or KVM/HAXM for HW accel)
| |
| +-- Virtual CPU (vCPU) executing ARM/x86/RISC-V instructions
| +-- Virtual memory management (shadow page tables / EPT)
| +-- Interrupt controller (GICv2/v3 for ARM, IOAPIC for x86)
|
+-- Goldfish/Ranchu virtual hardware
| +-- goldfish-pipe: host<->guest communication channel
| +-- virtio-gpu: GPU passthrough / host rendering
| +-- virtio-net: virtual networking
| +-- virtio-input: touch, keyboard, sensors
| +-- virtio-blk: block device emulation
| +-- virtio-console: serial/console ports
|
+-- Emulator UI / gRPC control interface
+-- Skin rendering, Extended Controls
+-- Snapshot management
+-- Location / Telephony / Battery simulation
58.1.2 Execution Modes¶
The emulator supports two fundamental execution modes:
-
KVM-accelerated mode (Linux): The guest code runs natively on the host CPU through the Kernel-based Virtual Machine (KVM) module. This is the fastest mode and is always preferred when the guest and host architectures match (x86 guest on x86 host, or ARM guest on ARM host). With KVM, most guest instructions execute at near-native speed. Only privileged operations (I/O, page table manipulation) trap to the emulator for handling.
-
Software translation mode (TCG): QEMU's Tiny Code Generator translates guest instructions to host instructions on the fly. This is used when architectures do not match (e.g., running an ARM guest image on an x86 host). While significantly slower than KVM, TCG enables cross-architecture development.
On macOS, Apple's Hypervisor Framework replaces KVM; on Windows, the Intel HAXM (Hardware Accelerated Execution Manager) or Windows Hypervisor Platform (WHPX) serves the same role.
58.1.3 High-Level Data Flow¶
graph TB
subgraph "Host Machine"
EMU["Android Emulator<br/>(QEMU fork)"]
KVM["KVM / HAXM / HVF"]
HOSTGPU["Host GPU Driver"]
UI["Emulator UI / gRPC"]
end
subgraph "Guest (Android)"
KERNEL["Linux Kernel<br/>(Ranchu)"]
GOLDFISH["Goldfish Virtual Devices"]
HAL["HAL Implementations<br/>(ranchu)"]
FRAMEWORK["Android Framework"]
APP["Applications"]
end
APP --> FRAMEWORK
FRAMEWORK --> HAL
HAL --> GOLDFISH
GOLDFISH --> KERNEL
KERNEL -->|"vCPU execution"| KVM
KVM --> EMU
KERNEL -->|"I/O traps"| EMU
EMU -->|"GPU commands"| HOSTGPU
EMU --> UI
style KVM fill:#e1f5fe
style EMU fill:#fff3e0
style KERNEL fill:#e8f5e9
The critical insight is that the emulator is not "simulating" Android -- it is running Android. The kernel is a real Linux kernel. The userspace is the same system image that ships on physical devices (or very close to it). The emulator's job is to provide the virtual hardware that this real software expects to find.
58.1.4 Key Source Directories¶
| Directory | Purpose |
|---|---|
device/generic/goldfish/ |
Goldfish virtual device definitions, HALs, init scripts |
device/google/cuttlefish/ |
Cuttlefish virtual device definitions |
device/generic/goldfish/hals/ |
Hardware Abstraction Layer implementations |
device/generic/goldfish/init/ |
Init RC scripts for emulator boot |
device/generic/goldfish/sepolicy/ |
SELinux policy for emulator-specific domains |
device/generic/goldfish/board/ |
Board configuration (architecture-specific) |
device/generic/goldfish/product/ |
Product configuration makefiles |
58.1.5 Product Configurations¶
The emulator defines several product targets, listed in
device/generic/goldfish/AndroidProducts.mk:
# Source: device/generic/goldfish/AndroidProducts.mk
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone16k_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64_minigbm.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64_riscv64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_tablet_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_tablet_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64_minigbm.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone16k_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64_riscv64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_slim_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_slim_arm64.mk
These targets cover x86_64, ARM64, and RISC-V architectures, as well as specialized form factors (phone, tablet, slim) and graphics backends (standard vs. minigbm).
58.2 The Goldfish Device Platform¶
"Goldfish" is the original virtual hardware platform for the Android Emulator. The name refers to the collection of virtual devices (timers, interrupt controllers, I/O buses, etc.) that QEMU presents to the guest kernel. Over time, Goldfish has evolved significantly -- most of the original custom Goldfish devices have been replaced by standard virtio devices in the modern "Ranchu" platform, but the name persists in the AOSP source tree.
58.2.1 Product Configuration Hierarchy¶
The Goldfish product configuration follows a layered inheritance pattern:
graph TD
PHONE["phone.mk"] --> HANDHELD["handheld.mk"]
PHONE --> BASE_PHONE["base_phone.mk"]
HANDHELD --> BASE_HANDHELD["base_handheld.mk"]
HANDHELD --> GENERIC_SYSTEM["generic_system.mk"]
HANDHELD --> HANDHELD_SYSTEM_EXT["handheld_system_ext.mk"]
HANDHELD --> AOSP_PRODUCT["aosp_product.mk"]
BASE_HANDHELD --> GENERIC["generic.mk"]
GENERIC --> VERSIONS["versions.mk"]
TABLET["tablet.mk"] --> HANDHELD
SLIM["slim_handheld.mk"] --> GENERIC
style PHONE fill:#ffecb3
style GENERIC fill:#c8e6c9
The root configuration file is device/generic/goldfish/product/generic.mk,
which establishes the core emulator device. From its contents:
# Source: device/generic/goldfish/product/generic.mk (selected lines)
PRODUCT_VENDOR_PROPERTIES += \
ro.hardware.power=ranchu \
ro.kernel.qemu=1 \
ro.soc.manufacturer=AOSP \
ro.soc.model=ranchu \
The property ro.kernel.qemu=1 is a well-known flag that the framework uses
to detect that it is running inside the emulator. The SoC model is reported as
"ranchu" -- the modern codename for the emulator's virtual platform.
58.2.2 Board Configuration¶
The board configuration lives in device/generic/goldfish/board/. Each
architecture variant has its own directory:
| Directory | Architecture |
|---|---|
board/emu64x/ |
x86_64 |
board/emu64a/ |
ARM64 |
board/emu64xr/ |
x86_64 + RISC-V (native bridge) |
board/emu64ar/ |
ARM64 + RISC-V (native bridge) |
board/emu64x16k/ |
x86_64, 16KB pages |
board/emu64a16k/ |
ARM64, 16KB pages |
All of them inherit from board/BoardConfigCommon.mk, which establishes
shared board-level settings:
# Source: device/generic/goldfish/board/BoardConfigCommon.mk (key excerpts)
TARGET_BOOTLOADER_BOARD_NAME := goldfish_$(TARGET_ARCH)
# Build OpenGLES emulation guest and host libraries
BUILD_EMULATOR_OPENGL := true
BUILD_QEMU_IMAGES := true
USE_OPENGL_RENDERER := true
# Emulator doesn't support sparse image format.
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
# emulator is Non-A/B device
AB_OTA_UPDATER := none
# emulator needs super.img
BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true
# 8G + 8M
BOARD_SUPER_PARTITION_SIZE ?= 8598323200
BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions
Key takeaways:
-
The emulator uses the super partition with dynamic partitions, matching modern physical devices.
-
It is a Non-A/B device (no seamless OTA dual partitions).
- OpenGL ES emulation libraries are built for both guest and host.
- The WiFi subsystem uses
NL80211with themac80211_hwsimkernel module to simulate a wireless network interface.
58.2.3 HAL Implementations¶
The heart of the Goldfish device platform is its collection of HAL
(Hardware Abstraction Layer) implementations. These are located under
device/generic/goldfish/hals/ and provide the bridge between Android's
hardware interfaces and the emulator's virtual devices.
graph LR
subgraph "Android Framework"
AF_AUDIO["AudioFlinger"]
AF_CAMERA["CameraService"]
AF_SENSORS["SensorService"]
AF_GNSS["LocationManager"]
AF_RADIO["RIL"]
AF_FP["FingerprintManager"]
AF_GFX["SurfaceFlinger"]
end
subgraph "Goldfish HALs"
HAL_AUDIO["audio@7.1-impl.ranchu"]
HAL_CAMERA["camera.provider.ranchu"]
HAL_SENSORS["sensors@2.1-impl.ranchu"]
HAL_GNSS["gnss-service.ranchu"]
HAL_RADIO["radio-service.ranchu"]
HAL_FP["fingerprint-service.ranchu"]
HAL_GFX["graphics.composer3-service.ranchu"]
HAL_GRALLOC["graphics.allocator-service.ranchu"]
end
subgraph "QEMU / Host"
QEMU_AUDIO["Host Audio"]
QEMU_CAMERA["Host Camera / Scene"]
QEMU_SENSORS["Emulator Sensors"]
QEMU_GPS["Emulator GPS"]
QEMU_MODEM["Modem Simulator"]
QEMU_FP["Fingerprint Sim"]
QEMU_GPU["Host GPU / SwiftShader"]
end
AF_AUDIO --> HAL_AUDIO --> QEMU_AUDIO
AF_CAMERA --> HAL_CAMERA --> QEMU_CAMERA
AF_SENSORS --> HAL_SENSORS --> QEMU_SENSORS
AF_GNSS --> HAL_GNSS --> QEMU_GPS
AF_RADIO --> HAL_RADIO --> QEMU_MODEM
AF_FP --> HAL_FP --> QEMU_FP
AF_GFX --> HAL_GFX --> QEMU_GPU
AF_GFX --> HAL_GRALLOC --> QEMU_GPU
style HAL_AUDIO fill:#e8f5e9
style HAL_CAMERA fill:#e8f5e9
style HAL_SENSORS fill:#e8f5e9
style HAL_GNSS fill:#e8f5e9
style HAL_RADIO fill:#e8f5e9
style HAL_FP fill:#e8f5e9
style HAL_GFX fill:#e8f5e9
style HAL_GRALLOC fill:#e8f5e9
58.2.3.1 Audio HAL¶
Location: device/generic/goldfish/hals/audio/
The audio HAL implements android.hardware.audio@7.1. It uses TinyALSA to
interface with a virtual sound card provided by QEMU. The implementation
includes:
-
primary_device.cpp-- The main audio device, handling volume control, mic mute, and stream creation. -
stream_out.cpp/stream_in.cpp-- Output and input stream implementations. -
talsa.cpp-- TinyALSA wrapper layer. device_port_sink.cpp/device_port_source.cpp-- Port abstraction for audio routing.
From device/generic/goldfish/hals/audio/primary_device.cpp:
// Source: device/generic/goldfish/hals/audio/primary_device.cpp
constexpr size_t kInBufferDurationMs = 15;
constexpr size_t kOutBufferDurationMs = 22;
Device::Device() {}
Return<Result> Device::initCheck() {
return Result::OK;
}
Return<Result> Device::setMasterVolume(float volume) {
if (isnan(volume) || volume < 0 || volume > 1.0) {
return FAILURE(Result::INVALID_ARGUMENTS);
}
mMasterVolume = volume;
updateOutputStreamVolume(mMasterMute ? 0.0f : volume);
return Result::OK;
}
The audio latency configuration is set in the product makefile:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_VENDOR_PROPERTIES += \
ro.hardware.audio.tinyalsa.period_count=4 \
ro.hardware.audio.tinyalsa.period_size_multiplier=2 \
ro.hardware.audio.tinyalsa.host_latency_ms=80 \
The 80ms host latency is higher than a physical device (which targets 5-20ms) because audio data must transit through QEMU's virtual sound card and the host's audio subsystem.
58.2.3.2 Camera HAL¶
Location: device/generic/goldfish/hals/camera/
The camera HAL implements the AIDL Camera Provider interface. It supports multiple camera sources:
-
QEMU cameras (
BaseQemuCamera,GasQemuCamera,MinigbmQemuCamera) -- cameras backed by the host's webcam or a virtual scene, communicated through QEMU's pipe mechanism. -
Fake rotating cameras (
FakeRotatingCamera) -- synthetic test cameras that render a rotating 3D pattern.
The communication with the QEMU host happens through the qemu_channel
abstraction. From device/generic/goldfish/hals/camera/qemu_channel.cpp:
// Source: device/generic/goldfish/hals/camera/qemu_channel.cpp
const char kServiceName[] = "camera";
base::unique_fd qemuOpenChannel() {
return base::unique_fd(qemud_channel_open(kServiceName));
}
int qemuRunQuery(const int fd,
const char* const query,
const size_t querySize,
std::vector<uint8_t>* result) {
int e = qemu_pipe_write_fully(fd, query, querySize);
if (e < 0) {
return FAILURE(e);
}
std::vector<uint8_t> reply;
e = qemuReceiveMessage(fd, &reply);
if (e < 0) {
return e;
}
// ... parse ok/ko response ...
}
The camera provider uses an ID scheme with the prefix
device@1.1/internal/:
// Source: device/generic/goldfish/hals/camera/CameraProvider.cpp
constexpr char kCameraIdPrefix[] = "device@1.1/internal/";
std::string getLogicalCameraId(const int index) {
char buf[sizeof(kCameraIdPrefix) + 8];
snprintf(buf, sizeof(buf), "%s%d", kCameraIdPrefix, index);
return buf;
}
58.2.3.3 Sensors HAL¶
Location: device/generic/goldfish/hals/sensors/
The sensors HAL is one of the most instructive examples of how the emulator
virtualizes hardware. It implements android.hardware.sensors@2.1 using
QEMU's sensor protocol.
Sensor List: The full list of emulated sensors is defined in
device/generic/goldfish/hals/sensors/sensor_list.cpp:
// Source: device/generic/goldfish/hals/sensors/sensor_list.cpp
const char* const kQemuSensorName[] = {
"acceleration",
"gyroscope",
"magnetic-field",
"orientation",
"temperature",
"proximity",
"light",
"pressure",
"humidity",
"magnetic-field-uncalibrated",
"gyroscope-uncalibrated",
"hinge-angle0",
"hinge-angle1",
"hinge-angle2",
"heart-rate",
"rgbc-light",
"wrist-tilt",
"acceleration-uncalibrated",
"heading",
};
This gives us 19 virtual sensors including:
| Sensor | Type | Reporting Mode |
|---|---|---|
| Accelerometer | ACCELEROMETER |
Continuous |
| Gyroscope | GYROSCOPE |
Continuous |
| Magnetic field | MAGNETIC_FIELD |
Continuous |
| Orientation | ORIENTATION |
Continuous |
| Temperature | AMBIENT_TEMPERATURE |
On-change |
| Proximity | PROXIMITY |
On-change + wake-up |
| Light | LIGHT |
On-change |
| Pressure | PRESSURE |
Continuous |
| Humidity | RELATIVE_HUMIDITY |
On-change |
| Hinge angle (x3) | HINGE_ANGLE |
On-change + wake-up |
| Heart rate | HEART_RATE |
On-change |
| Wrist tilt | WRIST_TILT_GESTURE |
Special + wake-up |
| Heading | Custom type 42 | Continuous |
Communication Protocol: The sensor HAL communicates with QEMU using a
simple text-based protocol over the QEMU pipe. From
device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp:
// Source: device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp
bool MultihalSensors::setSensorsReportingImpl(SensorsTransport& st,
const int sensorHandle,
const bool enabled) {
char buffer[64];
int len = snprintf(buffer, sizeof(buffer),
"set:%s:%d",
getQemuSensorNameByHandle(sensorHandle),
(enabled ? 1 : 0));
if (st.Send(buffer, len) < 0) {
ALOGE("%s:%d: send for %s failed", __func__, __LINE__, st.Name());
return false;
} else {
return true;
}
}
The protocol commands include:
list-sensors-- query which sensors the host supports (returns a bitmask)set:<sensor_name>:<0|1>-- enable or disable a sensorset-delay:<ms>-- set the reporting interval in millisecondstime:<ns>-- synchronize the guest clock
Parsing sensor events: When the host sends sensor data, the guest parses
text messages like acceleration:9.8:0.0:0.1. The parsing is done in
parseQemuSensorEventLocked:
// Source: device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp
void MultihalSensors::parseQemuSensorEventLocked(QemuSensorsProtocolState* state) {
char buf[256];
const int len = m_sensorsTransport->Receive(buf, sizeof(buf) - 1);
// ...
if (const char* values = testPrefix(buf, end, "acceleration", ':')) {
if (sscanf(values, "%f:%f:%f",
&vec3->x, &vec3->y, &vec3->z) == 3) {
vec3->status = SensorStatus::ACCURACY_MEDIUM;
event.timestamp = nowNs + state->timeBiasNs;
event.sensorHandle = kSensorHandleAccelerometer;
event.sensorType = SensorType::ACCELEROMETER;
postSensorEventLocked(event);
parsed = true;
}
}
// ... similar blocks for gyroscope, magnetic, proximity, light, etc.
}
The architecture uses a dedicated listener thread (qemuSensorListenerThread)
and a batch thread (batchThread) for continuous-mode sensors:
sequenceDiagram
participant EU as Emulator UI
participant QEMU as QEMU Host
participant PIPE as goldfish-pipe
participant HAL as Sensors HAL
participant SF as SensorService
EU->>QEMU: User rotates virtual phone
QEMU->>PIPE: "acceleration:0.0:9.8:0.0"
PIPE->>HAL: Data arrives on transport fd
HAL->>HAL: parseQemuSensorEventLocked()
HAL->>HAL: postSensorEventLocked()
HAL->>SF: IHalProxyCallback::postEvents()
SF->>SF: Dispatch to registered listeners
58.2.3.4 GNSS HAL¶
Location: device/generic/goldfish/hals/gnss/
The GNSS (Global Navigation Satellite System) HAL provides virtual GPS data.
The key class is GnssHwConn which opens a QEMU pipe to the "gps" service:
// Source: device/generic/goldfish/hals/gnss/GnssHwConn.cpp
GnssHwConn::GnssHwConn(IDataSink& sink) {
mDevFd.reset(qemu_pipe_open_ns("qemud", "gps", O_RDWR));
if (!mDevFd.ok()) {
ALOGE("%s:%d: qemu_pipe_open_ns failed", __func__, __LINE__);
return;
}
unique_fd threadsFd;
if (!::android::base::Socketpair(AF_LOCAL, SOCK_STREAM, 0,
&mCallersFd, &threadsFd)) {
ALOGE("%s:%d: Socketpair failed", __func__, __LINE__);
mDevFd.reset();
return;
}
std::promise<void> isReadyPromise;
const int devFd = mDevFd.get();
mThread = std::thread([devFd, threadsFd = std::move(threadsFd), &sink,
&isReadyPromise]() {
GnssHwListener listener(sink);
isReadyPromise.set_value();
workerThread(devFd, threadsFd.get(), listener);
});
isReadyPromise.get_future().wait();
}
The worker thread uses epoll to multiplex between the QEMU device fd (for
GPS data arriving from the host) and a command fd (for shutdown signals).
When the emulator's Extended Controls window sends a GPS fix, it flows
through QEMU's GPS service, through the pipe, into GnssHwListener which
parses NMEA sentences, and finally into Android's LocationManager.
The GNSS device node is created via a symlink in the init script:
# Source: device/generic/goldfish/init/init.ranchu.rc
on property:vendor.qemu.vport.gnss=*
symlink ${vendor.qemu.vport.gnss} /dev/gnss0
The corresponding SELinux policy grants the GNSS HAL access to vsock sockets:
# Source: device/generic/goldfish/sepolicy/vendor/hal_gnss_default.te
vndbinder_use(hal_gnss_default);
allow hal_gnss_default self:vsock_socket create_socket_perms_no_ioctl;
58.2.3.5 Radio (Telephony) HAL¶
Location: device/generic/goldfish/hals/radio/
The radio HAL implements the full suite of telephony AIDL interfaces:
RadioModem-- modem control (power on/off, IMEI, radio capability)RadioSim-- SIM card managementRadioNetwork-- network registration, signal strength, cell infoRadioData-- data calls and setupRadioVoice-- voice callsRadioMessaging-- SMSRadioIms-- IMS (IP Multimedia Subsystem)
The radio HAL communicates with a modem simulator using AT commands over a
channel. From device/generic/goldfish/hals/radio/RadioModem.cpp:
// Source: device/generic/goldfish/hals/radio/RadioModem.cpp
constexpr char kBasebandversion[] = "1.0.0.0";
constexpr char kModemUuid[] = "com.android.modem.simulator";
ScopedAStatus RadioModem::getBasebandVersion(const int32_t serial) {
NOT_NULL(mRadioModemResponse)->getBasebandVersionResponse(
makeRadioResponseInfo(serial), kBasebandversion);
return ScopedAStatus::ok();
}
The AT command interface uses a request-response pattern with an AtChannel
abstraction:
// Source: device/generic/goldfish/hals/radio/RadioModem.cpp
ScopedAStatus RadioModem::getImei(const int32_t serial) {
mAtChannel->queueRequester([this, serial](
const AtChannel::RequestPipe requestPipe) -> bool {
const AtResponsePtr response =
mAtConversation(requestPipe, atCmds::getIMEI,
[](const AtResponse& response) -> bool {
return response.holds<std::string>();
});
if (!response) {
NOT_NULL(mRadioModemResponse)->getImeiResponse(
makeRadioResponseInfo(serial,
FAILURE(RadioError::INTERNAL_ERR)), {});
return false;
} else if (const std::string* imeiSvn =
response->get_if<std::string>()) {
modem::ImeiInfo imeiInfo = {
.type = modem::ImeiInfo::ImeiType::PRIMARY,
.imei = imeiSvn->substr(0, 15),
.svn = imeiSvn->substr(15, 2),
};
NOT_NULL(mRadioModemResponse)->getImeiResponse(
makeRadioResponseInfo(serial), std::move(imeiInfo));
return true;
}
// ...
});
return ScopedAStatus::ok();
}
The radio HAL also supports 5G NR, LTE, TD-SCDMA, CDMA, EVDO, GSM, and WCDMA network types as configured in the product makefile:
# Source: device/generic/goldfish/product/generic.mk
# NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA
PRODUCT_VENDOR_PROPERTIES += ro.telephony.default_network=33
58.2.3.6 Fingerprint HAL¶
Location: device/generic/goldfish/hals/fingerprint/
The fingerprint HAL is a relatively simple implementation that provides
AIDL IFingerprint service. From device/generic/goldfish/hals/fingerprint/hal.cpp:
// Source: device/generic/goldfish/hals/fingerprint/hal.cpp
constexpr char HW_COMPONENT_ID[] = "FingerprintSensor";
constexpr char XW_VERSION[] = "ranchu/fingerprint/aidl";
constexpr char FW_VERSION[] = "1";
constexpr char SERIAL_NUMBER[] = "00000001";
constexpr char SW_COMPONENT_ID[] = "matchingAlgorithm";
ndk::ScopedAStatus Hal::getSensorProps(std::vector<SensorProps>* out) {
// ...
SensorProps props;
props.commonProps.sensorId = 0;
props.commonProps.sensorStrength = common::SensorStrength::STRONG;
props.commonProps.maxEnrollmentsPerUser =
Storage::getMaxEnrollmentsPerUser();
props.sensorType = FingerprintSensorType::REAR;
props.supportsNavigationGestures = false;
props.supportsDetectInteraction = true;
// ...
}
The emulator's Extended Controls window provides a virtual fingerprint scanner that triggers authentication events through this HAL.
58.2.3.7 Hardware Composer (HWC3) HAL¶
Location: device/generic/goldfish/hals/hwc3/
The hardware composer HAL has the richest implementation of all the emulator HALs. It supports two composition modes:
-
HostFrameComposer -- delegates composition to the host GPU, achieving hardware-accelerated rendering.
-
GuestFrameComposer -- performs composition within the guest using DRM (Direct Rendering Manager) and libyuv.
The host frame composer uses the gfxstream protocol to send composition commands to the emulator process:
// Source: device/generic/goldfish/hals/hwc3/HostFrameComposer.cpp
#include "gfxstream/guest/goldfish_sync.h"
#include "virtgpu_drm.h"
namespace aidl::android::hardware::graphics::composer3::impl {
// ...
static bool isMinigbmFromProperty() {
static constexpr const auto kGrallocProp = "ro.hardware.gralloc";
const auto grallocProp =
::android::base::GetProperty(kGrallocProp, "");
if (grallocProp == "minigbm") {
return true;
} else {
return false;
}
}
The guest frame composer is used as a fallback when host rendering is unavailable:
// Source: device/generic/goldfish/hals/hwc3/GuestFrameComposer.cpp
#include "Drm.h"
#include "Layer.h"
#include "DisplayFinder.h"
std::array<std::int8_t, 16> ToLibyuvColorMatrix(
const std::array<float, 16>& in) {
// Converts HAL color matrix to libyuv format
std::array<std::int8_t, 16> out;
for (int r = 0; r < 4; r++) {
for (int c = 0; c < 4; c++) {
int indexIn = (4 * r) + c;
int indexOut = (4 * c) + r;
float clampedValue = std::max(-128.0f,
std::min(127.0f, in[indexIn] * 64.0f + 0.5f));
out[indexOut] = static_cast<std::int8_t>(clampedValue);
}
}
return out;
}
The HWC3 implementation supports DRM-based display management with proper
plane, CRTC, and connector abstractions -- visible in the extensive list of
DRM-related source files: Drm.cpp, DrmClient.cpp, DrmConnector.cpp,
DrmCrtc.cpp, DrmDisplay.cpp, DrmPlane.cpp, DrmSwapchain.cpp,
DrmAtomicRequest.cpp, DrmBuffer.cpp, DrmEventListener.cpp,
DrmMode.cpp.
58.2.3.8 Gralloc (Graphics Allocator) HAL¶
Location: device/generic/goldfish/hals/gralloc/
The graphics allocator handles buffer allocation for both CPU and GPU
use. From device/generic/goldfish/hals/gralloc/allocator.cpp:
// Source: device/generic/goldfish/hals/gralloc/allocator.cpp
struct GoldfishAllocator : public BnAllocator {
GoldfishAllocator()
: mHostConn(HostConnection::createUnique(kCapsetNone))
, mDebugLevel(getDebugLevel()) {}
ndk::ScopedAStatus allocate2(const BufferDescriptorInfo& desc,
const int32_t count,
AllocationResult* const outResult) override {
// ...
const uint64_t usage = toUsage64(desc.usage);
if (needCpuBuffer(usage)) {
req.needImageAllocation = true;
// ...
} else {
req.needImageAllocation = false;
// ...
}
// ...
}
};
The key design decision is the split between CPU buffers (allocated in
guest memory via GoldfishAddressSpaceBlock) and GPU buffers (represented
as color buffers on the host). When a buffer needs GPU access, the allocator
creates a host-side color buffer through the render control encoder:
// Source: device/generic/goldfish/hals/gralloc/allocator.cpp
if (needGpuBuffer(req.usage)) {
hostHandleRefCountFd.reset(qemu_pipe_open("refcount"));
hostHandle = rcEnc.rcCreateColorBufferDMA(
&rcEnc, req.width, req.height,
req.glFormat, static_cast<int>(req.emuFwkFormat));
if (qemu_pipe_write(hostHandleRefCountFd.get(),
&hostHandle,
sizeof(hostHandle)) != sizeof(hostHandle)) {
rcEnc.rcCloseColorBuffer(&rcEnc, hostHandle);
return FAILURE(nullptr);
}
}
The allocator supports a wide range of pixel formats including RGBA_8888, RGB_565, RGBA_FP16, RGBA_1010102, YV12, YCBCR_420_888, and YCBCR_P010. The mapper library suffix is "ranchu":
ndk::ScopedAStatus getIMapperLibrarySuffix(std::string* outResult) override {
*outResult = "ranchu";
return ndk::ScopedAStatus::ok();
}
58.2.4 Init Scripts¶
The emulator's boot process is controlled by init scripts in
device/generic/goldfish/init/. The primary script is init.ranchu.rc:
# Source: device/generic/goldfish/init/init.ranchu.rc (key sections)
on early-init
mount proc proc /proc remount hidepid=2,gid=3009
setprop ro.cpuvulkan.version ${ro.boot.qemu.cpuvulkan.version}
setprop ro.hardware.egl ${ro.boot.hardwareegl:-emulation}
setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
setprop ro.opengles.version ${ro.boot.opengles.version}
setprop dalvik.vm.heapsize ${ro.boot.dalvik.vm.heapsize:-192m}
setprop debug.hwui.renderer ${ro.boot.debug.hwui.renderer:-skiagl}
setprop vendor.qemu.dev.bootcomplete 0
start vendor.dlkm_loader
on init
write /sys/block/zram0/comp_algorithm lz4
write /proc/sys/vm/page-cluster 0
start qemu-props
on post-fs-data
mkdir /data/vendor/var 0755 root root
mkdir /data/vendor/var/run 0755 root root
start ranchu-device-state
start ranchu-adb-setup
Several services are defined for emulator-specific functionality:
-
qemu-props-- reads boot properties from the emulator host and sets them as system properties. -
ranchu-adb-setup-- configures ADB for the emulator. -
ranchu-net-- sets up networking (VirtIO WiFi, inter-emulator connections). -
ranchu-setup-- performs post-boot-complete setup. -
goldfish-logcat-- forwards logcat output to the host via virtio console (/dev/hvc1). -
bt_vhci_forwarder-- forwards Bluetooth HCI traffic.
The rendering subsystem default is established at early-init:
# Source: device/generic/goldfish/init/init.ranchu.rc
setprop ro.hardware.egl ${ro.boot.hardwareegl:-emulation}
# default skiagl: skia uses gles to render
setprop debug.hwui.renderer ${ro.boot.debug.hwui.renderer:-skiagl}
# default skiaglthreaded
setprop debug.renderengine.backend \
${ro.boot.debug.renderengine.backend:-skiaglthreaded}
58.2.5 SELinux Policy¶
The emulator defines custom SELinux policy under
device/generic/goldfish/sepolicy/. The vendor policy directory contains
approximately 60 policy files covering all emulator-specific domains and
services.
Key policy files:
| File | Purpose |
|---|---|
qemu_props.te |
Policy for the qemu-props property-setting service |
hal_sensors_default.te |
Sensors HAL access to vsock sockets |
hal_gnss_default.te |
GNSS HAL access to vsock and vnode binder |
hal_radio_default.te |
Radio HAL modem simulator access |
hal_camera_default.te |
Camera HAL QEMU pipe access |
hal_graphics_composer_default.te |
HWC3 DRM and GPU access |
hal_graphics_allocator_default.te |
Gralloc buffer allocation policy |
goldfish_setup.te |
Emulator setup scripts |
goldfish_ip.te |
Network configuration |
From device/generic/goldfish/sepolicy/vendor/qemu_props.te:
# Source: device/generic/goldfish/sepolicy/vendor/qemu_props.te
type qemu_props, domain;
type qemu_props_exec, vendor_file_type, exec_type, file_type;
init_daemon_domain(qemu_props)
set_prop(qemu_props, qemu_hw_prop)
set_prop(qemu_props, qemu_sf_lcd_density_prop)
set_prop(qemu_props, vendor_qemu_prop)
set_prop(qemu_props, vendor_net_share_prop)
allow qemu_props self:vsock_socket create_socket_perms_no_ioctl;
allow qemu_props sysfs:dir read;
allow qemu_props sysfs:dir open;
allow qemu_props sysfs:file getattr;
allow qemu_props sysfs:file read;
allow qemu_props sysfs:file open;
The qemu_props domain is granted permission to set specific property
categories and to communicate over vsock (VirtIO socket) -- the primary
communication channel between the guest kernel and the QEMU host.
58.2.6 Detailed Package Inventory¶
The emulator's product configuration pulls in a significant number of packages.
Here is a categorized breakdown from
device/generic/goldfish/product/generic.mk:
Core Graphics Stack:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_PACKAGES += \
vulkan.ranchu \
libandroidemu \
libOpenglCodecCommon \
libOpenglSystemCommon \
android.hardware.graphics.composer3-service.ranchu
The vulkan.ranchu package provides the Vulkan ICD (Installable Client
Driver) for the emulator. libandroidemu and the OpenGL codec/system
libraries implement the guest-side portion of the GPU emulation pipeline.
OpenGL ES Emulation Libraries:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_PACKAGES += \
libGLESv1_CM_emulation \
lib_renderControl_enc \
libEGL_emulation \
libGLESv2_enc \
libvulkan_enc \
libGLESv2_emulation \
libGLESv1_enc \
libEGL_angle \
libGLESv1_CM_angle \
libGLESv2_angle
These libraries work in pairs:
-
lib*_emulation-- guest-side EGL/GLES implementation that intercepts API calls -
lib*_enc-- encoders that serialize GLES/Vulkan commands into a binary stream for transport to the host -
lib*_angle-- ANGLE-based implementation for Vulkan-backed GLES
Media Codecs:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_PACKAGES += \
android.hardware.media.c2@1.0-service-goldfish \
libcodec2_goldfish_vp8dec \
libcodec2_goldfish_vp9dec \
libcodec2_goldfish_avcdec \
libcodec2_goldfish_hevcdec
The goldfish media codecs use the Codec2 framework and delegate video decoding to the host through QEMU, enabling hardware-accelerated video playback even inside the emulator.
Compliance HALs ("Hello, world!" implementations):
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_PACKAGES += \
com.android.hardware.authsecret \
com.android.hardware.contexthub \
com.android.hardware.dumpstate \
android.hardware.health-service.example \
android.hardware.health.storage-service.default \
android.hardware.lights-service.example \
com.android.hardware.neuralnetworks \
com.android.hardware.power \
com.android.hardware.thermal \
com.android.hardware.vibrator
These are minimal implementations that satisfy CTS requirements. They do not perform real hardware operations but provide the AIDL service interfaces that the framework expects.
Conditional Package Selection:
The product makefile uses several build flags to conditionally include components:
| Flag | Default | Effect when true |
|---|---|---|
EMULATOR_DISABLE_RADIO |
false | Disables telephony HAL |
EMULATOR_VENDOR_NO_BIOMETRICS |
false | Disables fingerprint HAL |
EMULATOR_VENDOR_NO_GNSS |
false | Disables GNSS HAL |
EMULATOR_VENDOR_NO_SENSORS |
false | Disables sensors HAL |
EMULATOR_VENDOR_NO_CAMERA |
false | Disables camera HAL |
EMULATOR_VENDOR_NO_SOUND |
false | Disables audio HAL |
EMULATOR_VENDOR_NO_UWB |
false | Disables UWB HAL |
EMULATOR_VENDOR_NO_THREADNETWORK |
false | Disables Thread networking |
EMULATOR_VENDOR_NO_REBOOT_ESCROW |
false | Disables reboot escrow |
This allows building minimal emulator images for specialized testing where only specific subsystems are needed.
58.3 Virtual Hardware¶
58.3.1 The Goldfish-Pipe: Host-Guest Communication¶
The goldfish-pipe is the central communication mechanism between the Android guest and the QEMU host. It is a virtual device that provides a bidirectional byte-stream interface, similar to a Unix pipe but crossing the VM boundary.
graph LR
subgraph "Guest (Android)"
GHAL["HAL Implementation"]
GLIB["libqemu_pipe"]
KPIPE["goldfish-pipe<br/>kernel driver"]
end
subgraph "Host (QEMU)"
HPIPE["goldfish-pipe<br/>device model"]
HSVC["Host Service<br/>(camera, sensors, etc.)"]
end
GHAL -->|"qemu_pipe_open_ns()"| GLIB
GLIB -->|"open /dev/goldfish_pipe"| KPIPE
KPIPE -->|"MMIO / PIO"| HPIPE
HPIPE -->|"service dispatch"| HSVC
style KPIPE fill:#e1f5fe
style HPIPE fill:#fff3e0
Guest-side API: The pipe is accessed through the libqemu_pipe library,
which provides functions like:
-
qemu_pipe_open_ns(namespace, name, flags)-- opens a named pipe to a specific host service -
qemu_pipe_write_fully(fd, data, size)-- writes data to the pipe qemu_pipe_read_fully(fd, data, size)-- reads data from the pipe
The qemud layer adds multiplexing on top of the raw pipe. Multiple logical
channels can be opened through a single pipe connection. From
device/generic/goldfish/hals/lib/qemud/qemud.cpp:
// Source: device/generic/goldfish/hals/lib/qemud/qemud.cpp
int qemud_channel_open(const char* name) {
return qemu_pipe_open_ns("qemud", name, O_RDWR);
}
int qemud_channel_send(int pipe, const void* msg, int size) {
char header[5];
if (size < 0)
size = strlen((const char*)msg);
if (size == 0)
return 0;
if (size >= 64 * 1024) { // use binary encoding
uint32_t length32be = htonl(size | (1U << 31));
memcpy(header, &length32be, 4);
} else { // use hex digit encoding
snprintf(header, sizeof(header), "%04x", size);
}
if (qemu_pipe_write_fully(pipe, header, 4)) {
return -1;
}
if (qemu_pipe_write_fully(pipe, msg, size)) {
return -1;
}
return 0;
}
int qemud_channel_recv(int pipe, void* msg, int maxsize) {
char header[5];
int size;
if (qemu_pipe_read_fully(pipe, header, 4)) {
return -1;
}
header[4] = 0;
if (sscanf(header, "%04x", &size) != 1) {
return -1;
}
if (size > maxsize) {
return -1;
}
if (qemu_pipe_read_fully(pipe, msg, size)) {
return -1;
}
return size;
}
The protocol uses a simple length-prefixed framing:
- Messages under 64KB use a 4-character hex length prefix (e.g.,
"001a") - Messages of 64KB or larger use a 4-byte binary network-order length with the high bit set
58.3.2 The Sensor HAL Threading Model¶
The sensors HAL has a sophisticated multi-threaded architecture that merits
detailed examination. The MultihalSensors class header
(device/generic/goldfish/hals/sensors/include/multihal_sensors.h) reveals
the internal structure:
// Source: device/generic/goldfish/hals/sensors/include/multihal_sensors.h
struct MultihalSensors : public ahs21::implementation::ISensorsSubHal {
using SensorsTransportFactory =
std::function<std::unique_ptr<SensorsTransport>()>;
MultihalSensors(SensorsTransportFactory);
~MultihalSensors();
private:
struct QemuSensorsProtocolState {
int64_t timeBiasNs = -500000000;
int32_t sensorsUpdateIntervalMs = 200;
static constexpr float kSensorNoValue = -1e+30;
// on change sensors (host does not support them)
float lastAmbientTemperatureValue = kSensorNoValue;
float lastProximityValue = kSensorNoValue;
float lastLightValue = kSensorNoValue;
float lastRelativeHumidityValue = kSensorNoValue;
float lastHingeAngle0Value = kSensorNoValue;
float lastHingeAngle1Value = kSensorNoValue;
float lastHingeAngle2Value = kSensorNoValue;
float lastHeartRateValue = kSensorNoValue;
float lastWristTiltMeasurement = -1;
};
// batching
struct BatchEventRef {
int64_t timestamp = -1;
int sensorHandle = -1;
int generation = 0;
bool operator<(const BatchEventRef &rhs) const {
// not a typo: we want top() to be smallest timestamp
return timestamp > rhs.timestamp;
}
};
struct BatchInfo {
Event event;
int64_t samplingPeriodNs = 0;
int generation = 0;
};
QemuSensorsProtocolState m_protocolState;
std::priority_queue<BatchEventRef> m_batchQueue;
std::vector<BatchInfo> m_batchInfo;
std::condition_variable m_batchUpdated;
std::thread m_batchThread;
std::atomic<bool> m_batchRunning = true;
mutable std::mutex m_mtx;
};
The threading model has three threads:
-
Main thread -- handles HIDL/AIDL calls from SensorService (
activate,batch,flush,injectSensorData_2_1). -
Sensor listener thread (
qemuSensorListenerThread) -- reads sensor data from the QEMU transport and dispatches events. Usesepollto multiplex between the transport fd and a command fd. -
Batch thread (
batchThread) -- implements continuous-mode sensor batching. Uses a priority queue ordered by timestamp to determine when to deliver the next sensor event.
The epoll-based listener thread implementation from
device/generic/goldfish/hals/sensors/multihal_sensors_epoll.cpp:
// Source: device/generic/goldfish/hals/sensors/multihal_sensors_epoll.cpp
bool MultihalSensors::qemuSensorListenerThreadImpl(
const int transportFd) {
const unique_fd epollFd(epoll_create1(0));
epollCtlAdd(epollFd.get(), transportFd);
epollCtlAdd(epollFd.get(), m_sensorThreadFd.get());
while (true) {
struct epoll_event events[2];
const int kTimeoutMs = 60000;
const int n = TEMP_FAILURE_RETRY(epoll_wait(
epollFd.get(), events, 2, kTimeoutMs));
for (int i = 0; i < n; ++i) {
const struct epoll_event* ev = &events[i];
const int fd = ev->data.fd;
if (fd == transportFd) {
if (ev->events & EPOLLIN) {
std::unique_lock<std::mutex> lock(m_mtx);
parseQemuSensorEventLocked(&m_protocolState);
}
} else if (fd == m_sensorThreadFd.get()) {
const int cmd = qemuSensortThreadRcvCommand(fd);
switch (cmd) {
case kCMD_QUIT: return false;
case kCMD_RESTART: return true;
}
}
}
}
}
The kCMD_RESTART command is sent when the batch interval changes and the
transport needs to be reconfigured. The kCMD_QUIT command is sent during
shutdown. This design allows the sensor listener to be restarted without
tearing down the entire HAL.
graph TB
subgraph "MultihalSensors Threads"
MAIN["Main Thread<br/>(HIDL/AIDL calls)"]
LISTENER["Listener Thread<br/>(qemuSensorListenerThread)"]
BATCH["Batch Thread<br/>(batchThread)"]
end
subgraph "Synchronization"
MTX["m_mtx<br/>(std::mutex)"]
CMD["m_callersFd / m_sensorThreadFd<br/>(socketpair)"]
COND["m_batchUpdated<br/>(condition_variable)"]
end
subgraph "Data Structures"
MASK["m_activeSensorsMask"]
QUEUE["m_batchQueue<br/>(priority_queue)"]
INFO["m_batchInfo<br/>(vector<BatchInfo>)"]
end
MAIN -->|"lock m_mtx"| MTX
LISTENER -->|"lock m_mtx"| MTX
MAIN -->|"send command"| CMD
LISTENER -->|"receive command"| CMD
MAIN -->|"notify"| COND
BATCH -->|"wait"| COND
LISTENER -->|"write events"| INFO
BATCH -->|"read events, post"| INFO
MAIN -->|"push"| QUEUE
BATCH -->|"pop"| QUEUE
style LISTENER fill:#e1f5fe
style BATCH fill:#e8f5e9
style MAIN fill:#fff3e0
58.3.3 Display Discovery and VSync¶
The HWC3 HAL discovers displays by querying the QEMU host through the render
control encoder. From
device/generic/goldfish/hals/hwc3/DisplayFinder.cpp:
// Source: device/generic/goldfish/hals/hwc3/DisplayFinder.cpp
static uint32_t getVsyncHzFromProperty() {
static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
const auto vsyncProp =
::android::base::GetProperty(kVsyncProp, "");
uint64_t vsyncPeriod;
if (!::android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
return 60; // default 60 Hz
}
return static_cast<uint32_t>(vsyncPeriod);
}
HWC3::Error findGoldfishPrimaryDisplay(
std::vector<DisplayMultiConfigs>* outDisplays) {
DEFINE_AND_VALIDATE_HOST_CONNECTION
hostCon->lock();
const int32_t vsyncPeriodNanos =
HertzToPeriodNanos(getVsyncHzFromProperty());
DisplayMultiConfigs display;
display.displayId = 0;
if (rcEnc->hasHWCMultiConfigs()) {
int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
display.activeConfigId =
rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
for (int configId = 0; configId < count; configId++) {
display.configs.push_back(DisplayConfig(
configId,
rcEnc->rcGetFBDisplayConfigsParam(
rcEnc, configId, FB_WIDTH),
rcEnc->rcGetFBDisplayConfigsParam(
rcEnc, configId, FB_HEIGHT),
rcEnc->rcGetFBDisplayConfigsParam(
rcEnc, configId, FB_XDPI),
rcEnc->rcGetFBDisplayConfigsParam(
rcEnc, configId, FB_YDPI),
vsyncPeriodNanos));
}
}
// ...
}
The display finder queries the host for:
- Resolution (width, height) from
FB_WIDTHandFB_HEIGHTparameters - DPI (dots per inch) from
FB_XDPIandFB_YDPIparameters - Refresh rate from the
ro.boot.qemu.vsyncboot property
When the host supports multiple display configurations (multi-config mode), the display finder enumerates all available configurations and presents them to SurfaceFlinger through the HWC3 interface.
58.3.4 The Audio Write Thread¶
The audio HAL's output stream uses a dedicated write thread with FMQ (Fast
Message Queue) for low-latency communication with AudioFlinger. From
device/generic/goldfish/hals/audio/stream_out.cpp:
// Source: device/generic/goldfish/hals/audio/stream_out.cpp
class WriteThread : public IOThread {
typedef MessageQueue<IStreamOut::WriteCommand,
kSynchronizedReadWrite> CommandMQ;
typedef MessageQueue<IStreamOut::WriteStatus,
kSynchronizedReadWrite> StatusMQ;
typedef MessageQueue<uint8_t,
kSynchronizedReadWrite> DataMQ;
public:
WriteThread(StreamOut *stream, const size_t mqBufferSize)
: mStream(stream)
, mCommandMQ(1)
, mStatusMQ(1)
, mDataMQ(mqBufferSize, true /* EventFlag */) {
// ...
EventFlag* rawEfGroup = nullptr;
status_t status = EventFlag::createEventFlag(
mDataMQ.getEventFlagWord(), &rawEfGroup);
mEfGroup.reset(rawEfGroup);
mThread = std::thread(&WriteThread::threadLoop, this);
}
};
The FMQ mechanism allows AudioFlinger to write audio data without Binder
round-trips. The EventFlag provides lightweight signaling between the
AudioFlinger process and the HAL service process using shared memory.
58.3.5 Wake Lock Management¶
The emulator setup script manages power state through wake locks. From
device/generic/goldfish/init/init.setup.ranchu.sh:
# Source: device/generic/goldfish/init/init.setup.ranchu.sh
allowsuspend=`getprop ro.boot.qemu.allowsuspend`
case "$allowsuspend" in
"") echo "emulator_wake_lock" > /sys/power/wake_lock
;;
1) echo "emulator_wake_lock" > /sys/power/wake_unlock
;;
*) echo "emulator_wake_lock" > /sys/power/wake_lock
;;
esac
By default, the emulator holds a permanent wake lock (emulator_wake_lock)
to prevent the guest from entering deep sleep. This is important for
development because a suspended emulator would be unresponsive. The
ro.boot.qemu.allowsuspend=1 flag can be set to allow the guest to suspend,
which is useful for testing power management behavior.
58.3.6 The QEMU Properties Service¶
The qemu-props service (device/generic/goldfish/qemu-props/qemu-props.cpp)
is one of the first services started during emulator boot. It reads properties
from the emulator host and sets them as Android system properties:
// Source: device/generic/goldfish/qemu-props/qemu-props.cpp
constexpr char kBootPropertiesService[] = "boot-properties";
constexpr char kHeartbeatService[] = "QemuMiscPipe";
int setBootProperties() {
unique_fd qemud;
for (int tries = 5; tries > 0; --tries) {
qemud = unique_fd(qemud_channel_open(kBootPropertiesService));
if (qemud.ok()) break;
else if (tries > 1) sleep(1);
else return FAILURE(1);
}
if (qemud_channel_send(qemud.get(), "list", -1) < 0) {
return FAILURE(1);
}
while (true) {
char temp[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2];
const int len = qemud_channel_recv(qemud.get(), temp, sizeof(temp) - 1);
if (len < 0 || len > (sizeof(temp) - 1) || !temp[0]) break;
temp[len] = '\0';
char* prop_value = strchr(temp, '=');
if (!prop_value) continue;
*prop_value = 0;
++prop_value;
// Properties are prefixed with "vendor." unless already prefixed
// or in the system properties list
if (need_prepend_prefix(temp, "vendor.")) {
snprintf(renamed_property, sizeof(renamed_property),
"vendor.%s", temp);
final_prop_name = renamed_property;
}
property_set(final_prop_name, prop_value);
}
return 0;
}
After setting properties, the service enters a heartbeat loop, periodically
sending "heartbeat" messages to the QemuMiscPipe service. This allows the
emulator host to detect if the guest is alive and responsive:
// Source: device/generic/goldfish/qemu-props/qemu-props.cpp
int main(const int argc, const char* argv[]) {
if ((argc == 2) && !strcmp(argv[1], "bootcomplete")) {
sendMessage("bootcomplete");
return 0;
}
int r = setBootProperties();
parse_virtio_serial();
sendHeartBeat();
while (s_QemuMiscPipe >= 0) {
if (android::base::WaitForProperty(
"vendor.qemu.dev.bootcomplete", "1",
std::chrono::seconds(5))) {
break;
}
sendHeartBeat();
}
while (s_QemuMiscPipe >= 0) {
usleep(30 * 1000000); // 30 seconds
sendHeartBeat();
}
// ...
}
58.3.7 Virtual Sensors¶
The virtual sensors (covered in detail in section 27.2.3.3) are driven by the emulator's Extended Controls UI. When a user interacts with the sensor controls (tilting the virtual device, changing proximity, adjusting light level), the emulator host sends text-based sensor events through the QEMU pipe.
The sensor data flow:
sequenceDiagram
participant USER as User / Extended Controls
participant HOST as Emulator Host Process
participant PIPE as goldfish-pipe
participant HAL as Sensors HAL (Guest)
participant SVC as SensorService
participant APP as Application
USER->>HOST: Tilt device to 45 degrees
HOST->>HOST: Calculate acceleration vector
HOST->>PIPE: "acceleration:6.93:6.93:0.0"
PIPE->>HAL: Receive on transport fd
HAL->>HAL: parseQemuSensorEventLocked()
HAL->>HAL: postSensorEventLocked()
HAL->>SVC: IHalProxyCallback::postEvents()
SVC->>APP: onSensorChanged() callback
The sensor HAL adds calibration noise to uncalibrated sensor values to pass CTS (Compatibility Test Suite):
// Source: device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp
} else if (const char* values = testPrefix(buf, end,
"acceleration-uncalibrated", ':')) {
if (sscanf(values, "%f:%f:%f",
&uncal->x, &uncal->y, &uncal->z) == 3) {
// A little bias noise to pass CTS
uncal->x_bias = randomError(-0.003f, 0.003f);
uncal->y_bias = randomError(-0.003f, 0.003f);
uncal->z_bias = randomError(-0.003f, 0.003f);
// ...
}
}
58.3.8 Virtual GPS¶
GPS simulation flows through the GNSS HAL (section 27.2.3.4). The emulator supports:
- Fixed GPS coordinates (set via Extended Controls)
- GPS routes (GPX/KML file playback)
- NMEA sentence injection
The GPS service is registered at the QEMU host level as the "gps" qemud
service. The GnssHwListener class on the guest side parses incoming NMEA
data and dispatches it to the Android location framework.
58.3.9 Virtual Camera¶
The camera subsystem supports multiple virtual camera backends:
-
Host webcam passthrough -- The emulator captures frames from the host's webcam and sends them to the guest through the QEMU "camera" service.
-
Virtual scene -- A 3D-rendered environment that responds to the virtual device's orientation sensors.
-
Fake rotating camera -- A synthetic test pattern (class
FakeRotatingCameraindevice/generic/goldfish/hals/camera/).
Camera data transfer uses the same qemud protocol, with a query-response pattern:
Guest -> Host: "list" (list available cameras)
Host -> Guest: "ok:<camera_list>"
Guest -> Host: "connect:<id>" (connect to a specific camera)
Host -> Guest: "ok"
Guest -> Host: "start:<params>" (start capture)
Host -> Guest: "ok"
Host -> Guest: <frame_data> (raw frame data)
58.3.10 Virtual Telephony¶
The telephony subsystem uses a full modem simulator that communicates with the radio HAL via AT commands. The emulator supports:
- Voice calls (simulated call state machine)
- SMS sending and receiving
- Data connections
-
SIM card simulation (ICC profile files are loaded from
data/misc/modem_simulator/) -
Multiple radio access technologies (5G NR, LTE, GSM, etc.)
SIM profiles are pre-configured:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_COPY_FILES += \
device/generic/goldfish/hals/radio/data/apns-conf.xml:$(TARGET_COPY_OUT_VENDOR)/etc/apns/apns-conf.xml \
device/generic/goldfish/hals/radio/data/iccprofile_for_sim0.xml:data/misc/modem_simulator/iccprofile_for_sim0.xml \
device/generic/goldfish/hals/radio/data/numeric_operator.xml:data/misc/modem_simulator/etc/modem_simulator/files/numeric_operator.xml \
58.3.11 GPU Emulation¶
GPU emulation is one of the most architecturally complex parts of the emulator. The system supports three rendering modes:
graph TB
subgraph "Rendering Modes"
HOST["Host GPU Rendering<br/>(default, fastest)"]
SWIFT["SwiftShader<br/>(software, most compatible)"]
ANGLE["ANGLE<br/>(OpenGL ES on Vulkan)"]
end
subgraph "Guest Side"
GLES_EMU["libEGL_emulation<br/>libGLESv1_CM_emulation<br/>libGLESv2_emulation"]
GLES_ENC["libGLESv1_enc<br/>libGLESv2_enc<br/>libvulkan_enc"]
RENDER_CTRL["lib_renderControl_enc"]
end
subgraph "Host Side"
DECODE["Command Decoder"]
TRANSLATE["GL/VK Translation"]
HOST_DRV["Host GPU Driver"]
end
GLES_EMU --> GLES_ENC
GLES_ENC --> RENDER_CTRL
RENDER_CTRL -->|"goldfish-pipe"| DECODE
DECODE --> TRANSLATE
TRANSLATE --> HOST_DRV
style HOST fill:#c8e6c9
style SWIFT fill:#fff9c4
style ANGLE fill:#e1f5fe
Guest-side libraries (installed into the system image):
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_PACKAGES += \
libGLESv1_CM_emulation \
lib_renderControl_enc \
libEGL_emulation \
libGLESv2_enc \
libvulkan_enc \
libGLESv2_emulation \
libGLESv1_enc \
libEGL_angle \
libGLESv1_CM_angle \
libGLESv2_angle
The guest-side EGL/GLES libraries serialize OpenGL ES commands into a binary stream. This stream is sent to the host through the goldfish-pipe. On the host side, the emulator decodes these commands and replays them against the host's actual GPU driver.
When the host GPU is not available (e.g., on a headless CI server or a remote SSH session), SwiftShader provides a software implementation of Vulkan that runs entirely on the CPU.
ANGLE (Almost Native Graphics Layer Engine) provides an OpenGL ES implementation on top of Vulkan, which is useful for hosts that have Vulkan but not native OpenGL drivers (common on newer macOS systems).
The graphics rendering mode is selected via boot properties:
# Source: device/generic/goldfish/init/init.ranchu.rc
setprop ro.hardware.egl ${ro.boot.hardwareegl:-emulation}
setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
setprop ro.opengles.version ${ro.boot.opengles.version}
The gralloc HAL creates host-side GPU resources ("color buffers") through the render control encoder, as shown in section 27.2.3.8. This allows efficient zero-copy rendering where the guest composes frames that are directly displayed by the emulator's window.
58.4 Emulator Networking¶
58.4.1 Network Architecture¶
The emulator implements a virtual network that provides internet connectivity to the guest while isolating it from the host's physical network. The architecture uses a QEMU user-mode networking stack (SLIRP) by default, with an optional TAP-based networking mode for advanced use cases.
graph TB
subgraph "Guest Android"
ETH0["eth0 / wlan0"]
NET_STACK["Linux TCP/IP Stack"]
APPS["Applications"]
end
subgraph "Emulator Virtual Router"
ROUTER["Virtual Router<br/>10.0.2.1"]
DNS["Virtual DNS<br/>10.0.2.3"]
GW["Gateway to Host"]
end
subgraph "Host Machine"
HOST_NET["Host Network Stack"]
INTERNET["Internet"]
end
APPS --> NET_STACK
NET_STACK --> ETH0
ETH0 --> ROUTER
ROUTER --> DNS
ROUTER --> GW
GW --> HOST_NET
HOST_NET --> INTERNET
style ROUTER fill:#e1f5fe
style DNS fill:#e1f5fe
58.4.2 Default IP Addressing¶
Each emulator instance is assigned a unique IP address range:
| Component | Address |
|---|---|
| Virtual router/gateway | 10.0.2.1 |
| Host loopback alias | 10.0.2.2 |
| DNS server | 10.0.2.3 |
| Guest eth0 | 10.0.2.15 (DHCP assigned) |
58.4.3 VirtIO WiFi¶
Modern emulator builds use VirtIO WiFi instead of the legacy eth0 interface.
The networking script from
device/generic/goldfish/init/init.net.ranchu.sh handles this:
# Source: device/generic/goldfish/init/init.net.ranchu.sh
wifi_virtio=`getprop ro.boot.qemu.virtiowifi`
case "$wifi_virtio" in
1) wifi_mac_prefix=`getprop vendor.net.wifi_mac_prefix`
if [ -n "$wifi_mac_prefix" ]; then
/vendor/bin/mac80211_create_radios 1 $wifi_mac_prefix || exit 1
fi
;;
esac
When VirtIO WiFi is enabled, the mac80211_hwsim kernel module creates a
simulated WiFi radio. The wpa_supplicant service manages this virtual
interface:
# Source: device/generic/goldfish/init/init.ranchu.rc
service wpa_supplicant /vendor/bin/hw/wpa_supplicant \
-Dnl80211 -iwlan0 \
-c/vendor/etc/wifi/wpa_supplicant.conf \
-g@android:wpa_wlan0
interface aidl android.hardware.wifi.supplicant.ISupplicant/default
socket wpa_wlan0 dgram 660 wifi wifi
group system wifi inet
The VirtIO WiFi setup is triggered conditionally:
# Source: device/generic/goldfish/init/init.ranchu.rc
on post-fs-data && property:ro.boot.qemu.virtiowifi=1
start ranchu-net
58.4.4 Port Forwarding and ADB Connection¶
The emulator supports TCP and UDP port forwarding between the host and guest. ADB uses port forwarding to communicate with the guest:
- Console port: 5554 (first instance), 5556 (second), etc.
- ADB port: 5555 (first instance), 5557 (second), etc.
Port forwarding is configured through the emulator console:
# Forward host port 8080 to guest port 80
redir add tcp:8080:80
# Forward host port 5000 to guest port 5000
redir add tcp:5000:5000
The ADB daemon inside the guest listens on a well-known port. The emulator
automatically sets up the forwarding so that adb devices shows the emulator
instance as a connected device.
58.4.5 Inter-Emulator Networking¶
The networking script supports a secondary interface (eth1) for
inter-emulator communication:
# Source: device/generic/goldfish/init/init.net.ranchu.sh
# set up the second interface (for inter-emulator connections)
my_ip=`getprop vendor.net.shared_net_ip`
case "$my_ip" in
"")
;;
*) ifconfig eth1 "$my_ip" netmask 255.255.255.0 up
;;
esac
When multiple emulator instances need to communicate with each other (e.g.,
for testing multi-device scenarios), they can be configured with a shared
network where each instance gets a unique IP on the eth1 interface.
58.4.6 Bluetooth Networking¶
Bluetooth is emulated through VirtIO console devices. The init script creates a symlink for the Bluetooth device:
# Source: device/generic/goldfish/init/init.ranchu.rc
on property:vendor.qemu.vport.bluetooth=*
symlink ${vendor.qemu.vport.bluetooth} /dev/bluetooth0
service bt_vhci_forwarder \
/vendor/bin/bt_vhci_forwarder \
-virtio_console_dev=/dev/bluetooth0
class main
user bluetooth
group root bluetooth
The bt_vhci_forwarder service bridges between the VirtIO console device and
the Bluetooth VHCI (Virtual Host Controller Interface) driver, enabling the
guest to use the emulator's Bluetooth stack.
58.5 Ranchu vs. Goldfish Kernels¶
58.5.1 Historical Context¶
The emulator has gone through two major kernel generations:
-
Goldfish kernel (legacy): A custom-modified Linux kernel with Goldfish-specific device drivers for the original emulator virtual hardware (goldfish_timer, goldfish_fb, goldfish_audio, goldfish_battery, etc.).
-
Ranchu kernel (modern): A standard GKI (Generic Kernel Image) kernel that uses standard VirtIO devices instead of custom Goldfish devices. The name "ranchu" is a type of goldfish, reflecting the evolutionary relationship.
58.5.2 VirtIO Device Migration¶
The migration from custom Goldfish devices to standard VirtIO devices was a major architectural improvement:
graph TB
subgraph "Legacy Goldfish"
GF_FB["goldfish_fb<br/>(framebuffer)"]
GF_AUDIO["goldfish_audio"]
GF_BATTERY["goldfish_battery"]
GF_NET["goldfish_net"]
GF_PIPE["goldfish_pipe"]
GF_TIMER["goldfish_timer"]
end
subgraph "Modern Ranchu (VirtIO)"
V_GPU["virtio-gpu"]
V_SND["virtio-snd"]
V_INPUT["virtio-input"]
V_NET["virtio-net"]
V_BLK["virtio-blk"]
V_CONSOLE["virtio-console"]
V_RNG["virtio-rng"]
PIPE2["goldfish-pipe<br/>(retained)"]
end
GF_FB -.->|replaced by| V_GPU
GF_AUDIO -.->|replaced by| V_SND
GF_NET -.->|replaced by| V_NET
GF_PIPE -.->|retained| PIPE2
style GF_FB fill:#ffcdd2
style GF_AUDIO fill:#ffcdd2
style GF_BATTERY fill:#ffcdd2
style GF_NET fill:#ffcdd2
style V_GPU fill:#c8e6c9
style V_SND fill:#c8e6c9
style V_NET fill:#c8e6c9
style V_BLK fill:#c8e6c9
The goldfish-pipe device was retained because it serves a unique role that VirtIO does not directly address: a high-bandwidth, low-latency channel for serialized GPU commands and other bulk data transfers between guest HALs and host services.
58.5.3 Kernel Module Configuration¶
The modern Ranchu kernel uses loadable kernel modules for VirtIO devices. The Cuttlefish board configuration (which uses the same kernel) shows the required ramdisk modules:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
RAMDISK_KERNEL_MODULES ?= \
failover.ko \
nd_virtio.ko \
net_failover.ko \
virtio_dma_buf.ko \
virtio-gpu.ko \
virtio_input.ko \
virtio_net.ko \
virtio-rng.ko \
These are the modules that must be loaded in first-stage init for the system to boot. Additional modules loaded later include:
virtio_blk.ko-- block device emulationvirtio_console.ko-- serial console and virtual portsvirtio_pci.ko-- PCI transport for VirtIO-
vmw_vsock_virtio_transport.ko-- vsock transport for guest-host communication -
mac80211_hwsim.ko-- WiFi simulation cfg80211.ko,mac80211.ko-- wireless networking stack
58.5.4 Kernel Version Selection¶
The Cuttlefish configuration shows how kernel versions are selected:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
TARGET_KERNEL_USE ?= 6.12
SYSTEM_DLKM_SRC ?= \
kernel/prebuilts/$(TARGET_KERNEL_USE)/$(TARGET_KERNEL_ARCH)
KERNEL_MODULES_PATH ?= \
kernel/prebuilts/common-modules/virtual-device/\
$(TARGET_KERNEL_USE)/$(subst _,-,$(TARGET_KERNEL_ARCH))
TARGET_KERNEL_PATH ?= \
$(SYSTEM_DLKM_SRC)/kernel-$(TARGET_KERNEL_USE)
The default kernel version is 6.12, with prebuilt kernels stored under
kernel/prebuilts/. The common-modules/virtual-device/ directory contains
kernel modules specifically built for virtual device use.
58.5.5 ZRAM and Memory Configuration¶
The Ranchu kernel enables zram for memory compression:
# Source: device/generic/goldfish/init/init.ranchu.rc
on early-init
exec u:r:modprobe:s0 -- /system/bin/modprobe -a -d \
/system/lib/modules zram.ko
on init
write /sys/block/zram0/comp_algorithm lz4
write /proc/sys/vm/page-cluster 0
on sys-boot-completed-set && property:persist.sys.zram_enabled=1
swapon_all /vendor/etc/fstab.${ro.hardware}
The zram compression uses LZ4 for fast compression/decompression. The
page-cluster 0 setting tells the kernel to read one page at a time from
swap, which is optimal for zram since there is no seek penalty.
58.6 Cuttlefish: The Cloud-Friendly Alternative¶
58.6.1 What is Cuttlefish?¶
Cuttlefish is a configurable virtual Android device that runs in cloud environments without requiring a physical display, audio device, or any hardware-specific infrastructure. While Goldfish/Ranchu is designed primarily for the Android Studio emulator (a desktop application with a GUI), Cuttlefish is designed for server-side use cases: CI/CD, automated testing, cloud gaming, and development on remote machines.
Location: device/google/cuttlefish/
58.6.2 Architecture Comparison¶
graph TB
subgraph "Goldfish / Ranchu"
GF_EMU["Android Emulator<br/>(QEMU + UI)"]
GF_GUEST["Android Guest"]
GF_DISPLAY["Desktop Window"]
GF_CTRL["Extended Controls UI"]
end
subgraph "Cuttlefish"
CF_CVD["CVD (Cuttlefish Virtual Device)"]
CF_GUEST["Android Guest"]
CF_WEBRTC["WebRTC Display"]
CF_ADB["ADB / CLI Control"]
CF_HOST["Host Tooling<br/>(launch_cvd, stop_cvd)"]
end
GF_EMU --> GF_GUEST
GF_EMU --> GF_DISPLAY
GF_EMU --> GF_CTRL
CF_CVD --> CF_GUEST
CF_CVD --> CF_WEBRTC
CF_CVD --> CF_ADB
CF_HOST --> CF_CVD
style GF_EMU fill:#fff3e0
style CF_CVD fill:#e8f5e9
| Feature | Goldfish/Ranchu | Cuttlefish |
|---|---|---|
| Primary use case | Desktop development | Cloud / CI |
| Display | Desktop window | WebRTC or VNC |
| Audio | Host audio output | Virtual audio |
| GPU | Host GPU passthrough | virtio-gpu / SwiftShader |
| Networking | User-mode (SLIRP) | TAP / bridge |
| Multi-instance | Multiple processes | launch_cvd --num_instances=N |
| OTA updates | Not supported | A/B updates supported |
| Snapshotting | QEMU snapshots | Not a primary feature |
| Form factors | Phone, Tablet | Phone, TV, Auto, Wear |
| Architecture | x86_64, ARM64, RISC-V | x86_64, ARM64, RISC-V |
58.6.3 Cuttlefish Device Targets¶
From device/google/cuttlefish/AndroidProducts.mk and the directory
structure, Cuttlefish supports a wider array of architectures and form factors:
| Directory | Architecture |
|---|---|
vsoc_x86_64/ |
x86_64 |
vsoc_arm64/ |
ARM64 |
vsoc_riscv64/ |
RISC-V 64-bit |
vsoc_x86_64_only/ |
x86_64 (64-bit only) |
vsoc_arm64_only/ |
ARM64 (64-bit only) |
vsoc_x86_64_minidroid/ |
Minimal x86_64 |
vsoc_arm64_minidroid/ |
Minimal ARM64 |
vsoc_riscv64_minidroid/ |
Minimal RISC-V |
vsoc_arm64_pgagnostic/ |
ARM64 page-size agnostic |
The "vsoc" prefix stands for "Virtual System on Chip."
58.6.4 Host Tooling¶
Cuttlefish includes an extensive suite of host-side tools under
device/google/cuttlefish/host/commands/:
| Tool | Purpose |
|---|---|
start/ |
Launch the virtual device |
stop/ |
Stop the virtual device |
run_cvd/ |
Core virtual device runtime |
assemble_cvd/ |
Assemble disk images and configuration |
cvd_env/ |
Environment management |
console_forwarder/ |
Serial console forwarding |
kernel_log_monitor/ |
Kernel log monitoring |
log_tee/ |
Log tee-ing and forwarding |
logcat_receiver/ |
Logcat reception from guest |
modem_simulator/ |
Modem simulation for telephony |
gnss_grpc_proxy/ |
GNSS data proxy via gRPC |
display/ |
Display management |
screen_recording_server/ |
Screen recording service |
record_cvd/ |
Recording utility |
secure_env/ |
Security environment (KeyMint, etc.) |
sensors_simulator/ |
Sensor simulation |
health/ |
Device health monitoring |
host_bugreport/ |
Bug report collection |
metrics/ |
Metrics collection |
snapshot_util_cvd/ |
Snapshot management |
powerbtn_cvd/ |
Power button simulation |
powerwash_cvd/ |
Factory reset simulation |
cvd_send_sms/ |
SMS injection |
cvd_update_location/ |
Location update injection |
58.6.5 Board Configuration Differences¶
The Cuttlefish board configuration
(device/google/cuttlefish/shared/BoardConfig.mk) differs from Goldfish in
several important ways:
A/B OTA support:
More dynamic partitions:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_SUPER_PARTITION_SIZE := 8589934592 # 8GiB
BOARD_SUPER_PARTITION_GROUPS := \
google_system_dynamic_partitions \
google_vendor_dynamic_partitions
BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_PARTITION_LIST := \
product system system_ext system_dlkm
BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_PARTITION_LIST := \
odm vendor vendor_dlkm odm_dlkm
Separate ODM and vendor_dlkm partitions:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_USES_ODMIMAGE := true
BOARD_USES_VENDOR_DLKMIMAGE := true
BOARD_USES_ODM_DLKMIMAGE := true
BOARD_USES_SYSTEM_DLKMIMAGE := true
Kernel command line customization:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_KERNEL_CMDLINE += printk.devkmsg=on
BOARD_KERNEL_CMDLINE += audit=1
BOARD_KERNEL_CMDLINE += panic=-1
BOARD_KERNEL_CMDLINE += 8250.nr_uarts=1
BOARD_KERNEL_CMDLINE += binder.impl=rust
BOARD_KERNEL_CMDLINE += cma=0
BOARD_KERNEL_CMDLINE += firmware_class.path=/vendor/etc/
BOARD_KERNEL_CMDLINE += loop.max_part=7
BOARD_KERNEL_CMDLINE += init=/init
BOARD_BOOTCONFIG += androidboot.hardware=cutf_cvm
Notable Cuttlefish-specific kernel parameters:
binder.impl=rust-- Uses the Rust binder driver implementation.cma=0-- Disables Contiguous Memory Allocator (not needed in a VM).panic=-1-- Reboots immediately on kernel panic.
58.6.6 Getting Started with Cuttlefish¶
From the official device/google/cuttlefish/README.md:
# 1. Ensure KVM is available
grep -c -w "vmx\|svm" /proc/cpuinfo
# 2. Install host packages
sudo apt install -y git devscripts config-package-dev \
debhelper-compat golang curl
git clone https://github.com/google/android-cuttlefish
cd android-cuttlefish
./tools/buildutils/build_packages.sh
sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f
sudo dpkg -i ./cuttlefish-user_*_*64.deb || sudo apt-get install -f
sudo usermod -aG kvm,cvdnetwork,render $USER
sudo reboot
# 3. Download images from ci.android.com
# 4. Launch
mkdir cf && cd cf
tar xvf /path/to/cvd-host_package.tar.gz
unzip /path/to/aosp_cf_x86_64_phone-img-xxxxxx.zip
HOME=$PWD ./bin/launch_cvd
# 5. Access via WebRTC at https://localhost:8443
# 6. Debug with ADB
./bin/adb -e shell
# 7. Stop
HOME=$PWD ./bin/stop_cvd
58.6.7 Cuttlefish VirtIO Module Dependencies¶
The Cuttlefish board configuration illustrates the full VirtIO stack required for a virtual device. The modules are split between ramdisk (first-stage init) and vendor partition (second-stage init):
Ramdisk modules (required for boot):
# Source: device/google/cuttlefish/shared/BoardConfig.mk
RAMDISK_KERNEL_MODULES ?= \
failover.ko \
nd_virtio.ko \
net_failover.ko \
virtio_dma_buf.ko \
virtio-gpu.ko \
virtio_input.ko \
virtio_net.ko \
virtio-rng.ko \
These modules must be available in first-stage init because:
virtio-gpu.ko-- required for display outputvirtio_net.ko-- required for network access during provisioningvirtio_input.ko-- required for input eventsnd_virtio.ko-- VirtIO NUMA distance supportvirtio-rng.ko-- random number generation (required for crypto init)
Transport modules:
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
$(SYSTEM_VIRTIO_PREBUILTS_PATH)/virtio_blk.ko \
$(SYSTEM_VIRTIO_PREBUILTS_PATH)/virtio_console.ko \
$(SYSTEM_VIRTIO_PREBUILTS_PATH)/virtio_pci.ko \
$(SYSTEM_VIRTIO_PREBUILTS_PATH)/vmw_vsock_virtio_transport.ko
The VirtIO PCI transport module is the base for all VirtIO devices when
running on a PCI bus (which is the case for QEMU on x86). On ARM, VirtIO
MMIO transport (virtio_mmio.ko) may be used instead.
WiFi modules (mac80211 stack):
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
$(wildcard $(SYSTEM_DLKM_SRC)/cfg80211.ko) \
$(wildcard $(SYSTEM_DLKM_SRC)/libarc4.ko) \
$(wildcard $(SYSTEM_DLKM_SRC)/mac80211.ko) \
$(wildcard $(SYSTEM_DLKM_SRC)/rfkill.ko) \
$(wildcard $(KERNEL_MODULES_PATH)/mac80211_hwsim.ko)
The mac80211_hwsim module provides software-simulated WiFi radios. This
module is loaded in first-stage init with mac80211_hwsim.radios=0 to avoid
creating unwanted radios; the actual radio creation is done later by the
mac80211_create_radios tool.
58.6.8 Cuttlefish vs Goldfish: Architectural Differences¶
graph TB
subgraph "Goldfish Architecture"
GF_QEMU["QEMU Process<br/>(single binary)"]
GF_UI["Emulator UI<br/>(Qt/SDL)"]
GF_GUEST["Guest Android"]
GF_QEMU --> GF_GUEST
GF_QEMU --> GF_UI
end
subgraph "Cuttlefish Architecture"
CF_LAUNCH["launch_cvd<br/>(orchestrator)"]
CF_CVD["run_cvd<br/>(VM manager)"]
CF_WEBRTC["WebRTC Server"]
CF_MODEM["Modem Simulator"]
CF_GNSS["GNSS Proxy"]
CF_LOG["Log Monitors"]
CF_GUEST2["Guest Android"]
CF_LAUNCH --> CF_CVD
CF_LAUNCH --> CF_WEBRTC
CF_LAUNCH --> CF_MODEM
CF_LAUNCH --> CF_GNSS
CF_LAUNCH --> CF_LOG
CF_CVD --> CF_GUEST2
end
style GF_QEMU fill:#fff3e0
style CF_LAUNCH fill:#e8f5e9
Key architectural difference: Goldfish is a monolithic QEMU process that handles everything (VM, display, device emulation), while Cuttlefish uses a microservice architecture where each function (VM management, display, modem, GNSS, logging) runs as a separate process. This makes Cuttlefish more modular and easier to debug, but also more complex to set up.
58.6.9 When to Use Which¶
| Scenario | Recommended |
|---|---|
| Android Studio development | Goldfish/Ranchu (emulator) |
| Local debugging with UI | Goldfish/Ranchu (emulator) |
| CI/CD automated testing | Cuttlefish |
| Cloud-based development | Cuttlefish |
| Performance testing | Cuttlefish (more deterministic) |
| CTS/VTS testing | Cuttlefish (primary reference) |
| Multi-instance testing | Cuttlefish |
| Snapshot-based workflows | Goldfish/Ranchu (emulator) |
| Foldable device testing | Both (Goldfish has richer UI) |
| Automotive / TV / Wear | Cuttlefish (more form factors) |
Cuttlefish is increasingly becoming the primary virtual reference device in AOSP. Google uses it internally for continuous testing, and it is the recommended target for platform developers who do not need the interactive GUI features of the Android Studio emulator.
58.6.10 Crosvm Device Architecture¶
Cuttlefish's default VMM is crosvm (Chrome OS Virtual Machine monitor),
a Rust-based VMM originally developed for Chrome OS. The VM manager code at
device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp (1076 lines)
constructs the crosvm command line with all virtio device parameters.
Virtio Device Map¶
Every I/O device exposed to the guest is a virtio device over PCI transport:
graph TB
subgraph Guest["Cuttlefish Guest VM"]
K["Linux Kernel"]
K --> VG["virtio-gpu.ko<br/>PCI 00:02.0"]
K --> VN["virtio-net.ko<br/>PCI 00:01.1-3"]
K --> VI["virtio-input.ko"]
K --> VB["virtio-blk.ko"]
K --> VS["vsock.ko"]
K --> VR["virtio-rng.ko"]
K --> VC["virtio-console.ko<br/>18 HVC ports"]
end
subgraph Host["Cuttlefish Host"]
CROSVM["crosvm"]
CROSVM --> WGPU["vhost-user-gpu<br/>Wayland + gfxstream"]
CROSVM --> VTAP["TAP device<br/>vhost-net"]
CROSVM --> VINP["vhost-user-input<br/>keyboard/mouse/touch"]
CROSVM --> VBLK["Composite Disk<br/>system + data"]
CROSVM --> VVSOCK["vhost-device-vsock"]
CROSVM --> VRNG["Built-in RNG"]
CROSVM --> VCON["Serial sockets<br/>to host daemons"]
end
VG <-.->|"PCI"| CROSVM
VN <-.->|"PCI"| CROSVM
VI <-.->|"PCI"| CROSVM
VB <-.->|"PCI"| CROSVM
VS <-.->|"PCI"| CROSVM
VR <-.->|"PCI"| CROSVM
VC <-.->|"PCI"| CROSVM
PCI Slot Assignments¶
// Source: device/google/cuttlefish/host/libs/vm_manager/vm_manager.h:86-89
static const int kNetPciDeviceNum = 1; // Network on PCI slot 1
static const int kGpuPciSlotNum = 2; // GPU on PCI slot 2
static const int kDefaultNumBootDevices = 2;
static const int kMaxDisks = 3;
Network interfaces are assigned sub-addresses on PCI slot 1:
| Interface | PCI Address | TAP Device | Purpose |
|---|---|---|---|
| Mobile | 00:01.1 | cvd-mtap-NN |
Cellular data simulation |
| Ethernet | 00:01.2 | cvd-etap-NN |
Wired network |
| WiFi | 00:01.3 | cvd-wtap-NN |
Wireless (optional) |
58.6.11 Vhost-User Device Model¶
Cuttlefish uses the vhost-user protocol to run device backends as separate host processes, rather than inside the crosvm process. This provides better isolation, independent restartability, and allows device backends to be written in different languages (the input device is in Rust, the audio server in C++).
graph LR
subgraph crosvm["crosvm process"]
VQ["Virtqueue<br/>ring buffer"]
end
subgraph vhost["vhost-user process"]
DEV["Device Backend<br/>(GPU / Input / Block)"]
end
VQ <-->|"Unix socket<br/>vhost-user protocol"| DEV
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_builder.h:64
void AddVhostUser(const std::string& type, const std::string& socket_path,
int max_queue_size = 256);
Supported vhost-user device types:
| Type | Backend Process | Socket Path | Purpose |
|---|---|---|---|
gpu |
vhost-user-gpu | gpu_socket_path() |
Graphics rendering |
input |
vhost_user_input (Rust) | keyboard_socket_path() etc. |
Input events |
vsock |
vhost-device-vsock | vhost_user_vsock_path() |
Host-guest communication |
block |
vhost-user-block | disk socket | Storage (disk 2 only) |
mac80211-hwsim |
WiFi simulator | hwsim socket | WiFi radio simulation |
The default virtqueue size is 256 entries (must be a power of 2).
58.6.12 HVC Port Map¶
Cuttlefish uses 18 Hypervisor Virtual Console (HVC) ports to tunnel
communication between guest HALs and host-side daemons. Each HVC port
appears as /dev/hvcN in the guest:
| Port | Guest Device | Host Endpoint | Purpose |
|---|---|---|---|
/dev/hvc0 |
Kernel console | kernel_log_pipe |
Kernel output |
/dev/hvc1 |
Serial console | console_pipe |
Android serial console |
/dev/hvc2 |
Logcat | logcat_pipe |
System log forwarding |
/dev/hvc3 |
Keymaster | secure_env daemon |
C++ KeyMaster HAL |
/dev/hvc4 |
Gatekeeper | secure_env daemon |
Lock screen verification |
/dev/hvc5 |
Bluetooth | root_canal simulator |
Bluetooth HCI channel |
/dev/hvc6 |
GNSS | gnss_grpc_proxy |
GPS/GNSS location data |
/dev/hvc7 |
Location | Location daemon | Injected location fixes |
/dev/hvc8 |
ConfirmationUI | Trusty integration | Secure confirmation dialogs |
/dev/hvc9 |
UWB | UWB daemon | Ultra-Wideband ranging |
/dev/hvc10 |
OEMLock | OEMLock daemon | OEM bootloader unlock |
/dev/hvc11 |
KeyMint | secure_env daemon |
Rust KeyMint HAL |
/dev/hvc12 |
NFC | NFC daemon | NFC emulation |
/dev/hvc13 |
Sensors | sensors_simulator |
Accelerometer/gyro/etc. |
/dev/hvc14 |
MCU control | MCU daemon | Microcontroller control |
/dev/hvc15 |
MCU UART | MCU daemon | Microcontroller serial |
/dev/hvc16 |
Ti50 TPM | TPM daemon | TPM FIFO commands |
/dev/hvc17 |
JCardSim | Java Card simulator | eSIM/secure element |
Each HVC port is backed by either a Unix socket or a pipe on the host side:
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_builder.h:42-45
void AddHvcSink(); // null device (unused port)
void AddHvcReadOnly(Fd output, bool console); // one-way (kernel logs)
void AddHvcReadWrite(Fd output, Fd input); // bidirectional
void AddHvcSocket(const std::string& socket); // Unix socket
58.6.13 GPU Pipeline and Display Modes¶
Cuttlefish supports multiple GPU rendering modes, configured via the
--gpu_mode flag:
| Mode | Description |
|---|---|
gfxstream |
Host GPU passthrough via gfxstream protocol (default) |
gfxstream_guest_angle |
ANGLE in guest, gfxstream transport to host GPU |
drm_virgl |
Virgl3D — OpenGL commands forwarded via virtio-gpu DRM |
guest_swiftshader |
Pure software rendering in guest (SwiftShader Vulkan) |
none |
No GPU — headless mode |
Display Architecture¶
graph LR
subgraph Guest["Guest VM"]
APP["Android App"] --> SF["SurfaceFlinger"]
SF --> HWC["HWComposer HAL"]
HWC --> VGPU["virtio-gpu driver"]
end
subgraph Host["Host"]
VHGPU["vhost-user-gpu"] --> WAYLAND["Wayland Compositor"]
WAYLAND --> WEB["WebRTC Server"]
WAYLAND --> DISP["Local Display"]
end
VGPU <-->|"virtio-gpu<br/>PCI 00:02.0"| VHGPU
WEB -->|"Browser"| USER["Developer Browser"]
The gfxstream mode achieves near-native GPU performance by forwarding OpenGL ES / Vulkan commands directly to the host GPU driver. The virtio-gpu device acts as a transport channel rather than a GPU emulator.
The Wayland compositor receives rendered frames and can forward them to:
- WebRTC — streaming to a browser (cloud use case)
- Local display — direct rendering on the host screen
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:474-495
// Display configuration with width, height, DPI, and refresh rate
// Frames sent via Wayland socket to compositor
58.6.14 Networking Architecture¶
TAP Devices and Bridge Configuration¶
Cuttlefish creates TAP (network tap) devices on the host for each network interface, bridging guest virtio-net to the host network:
graph LR
subgraph Guest["Guest VM"]
RMIL["RIL<br/>Mobile Data"] --> VN1["virtio-net<br/>00:01.1"]
ETH["EthernetManager"] --> VN2["virtio-net<br/>00:01.2"]
WIFI["WiFi<br/>mac80211_hwsim"] --> VN3["virtio-net<br/>00:01.3"]
end
subgraph Host["Host"]
TAP1["cvd-mtap-NN"] --> BR["Network Bridge"]
TAP2["cvd-etap-NN"] --> BR
TAP3["cvd-wtap-NN"] --> BR
BR --> HOST_NET["Host Network"]
end
VN1 <--> TAP1
VN2 <--> TAP2
VN3 <--> TAP3
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:707-727
// Mobile TAP: PCI 00:01:01
// Ethernet TAP: PCI 00:01:02
// WiFi TAP: auto-assigned PCI (optional)
WiFi Simulation¶
Cuttlefish supports two WiFi simulation modes:
- TAP bridge — simple network bridging (no WiFi-specific behavior)
- mac80211_hwsim — kernel module that simulates 802.11 radios, enabling real WiFi scanning, association, and WPA authentication within the VM
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp
// WiFi via mac80211_hwsim when config.virtio_mac80211_hwsim() is true
vhost-net Acceleration¶
When enabled, vhost-net moves network packet processing from crosvm
userspace into the host kernel, significantly improving network throughput:
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:591
if (instance.vhost_net()) {
crosvm_cmd.Cmd().AddParameter("--vhost-net");
}
58.6.15 Guest HALs¶
Cuttlefish implements 21 HALs that bridge Android's HAL interfaces to host-side daemons via virtio devices, vsock, or HVC serial ports:
device/google/cuttlefish/guest/hals/
├── audio/ # virtio-snd / audio server
├── bluetooth/ # HVC → root_canal simulator
├── camera/ # vsock → host camera streaming
├── confirmationui/ # HVC → Trusty integration
├── gatekeeper/ # HVC → secure_env daemon
├── health/ # Battery/charge monitoring
├── identity/ # Identity credential HAL
├── keymint/ # HVC → secure_env (KeyMint)
├── light/ # vsock → light control (Rust)
├── nfc/ # HVC → NFC daemon
├── oemlock/ # HVC → OEM unlock
├── ril/ # HVC → modem_simulator (telephony)
├── secure_element/ # eSIM / secure chip access
├── sensors/ # HVC → sensors_simulator
├── vehicle/ # vsock → automotive VHAL
└── vulkan/ # Graphics support
Each guest HAL typically reads/writes a virtio-console device (/dev/hvcN)
or establishes a vsock connection to its host-side counterpart. The HAL
interface exposed to Android frameworks is identical to what a real hardware
HAL would provide — the virtualization is transparent to higher layers.
Example: Camera HAL via Vsock¶
The camera HAL streams frames from the host via vsock, allowing the host webcam to appear as the guest's camera:
// Source: device/google/cuttlefish/guest/hals/camera/vsock_camera_server.cpp
// Receives MJPEG/H264 frames from host over vsock
// Exposes standard Camera2 HAL interface to CameraService
Example: Light HAL via Vsock (Rust)¶
// Source: device/google/cuttlefish/guest/hals/light/lights_vsock_server.rs
// Receives light state changes over vsock
// Controls notification LED, backlight, etc.
58.6.16 Host Microservice Orchestration¶
Cuttlefish runs as a collection of ~48 host processes orchestrated by
launch_cvd and run_cvd:
graph TB
LAUNCH["launch_cvd<br/>Orchestrator"]
LAUNCH --> ASSEMBLE["assemble_cvd<br/>Disk assembly & config"]
LAUNCH --> RUN["run_cvd<br/>VM runtime manager"]
RUN --> CROSVM["crosvm<br/>VMM"]
RUN --> WEBRTC["webrtc_server<br/>Display streaming"]
RUN --> MODEM["modem_simulator<br/>RIL backend"]
RUN --> GNSS["gnss_grpc_proxy<br/>Location"]
RUN --> SENSORS["sensors_simulator<br/>Accelerometer/gyro"]
RUN --> SECURE["secure_env<br/>KeyMint/Gatekeeper"]
RUN --> VHINPUT["vhost_user_input<br/>Keyboard/mouse"]
RUN --> VHVSOCK["vhost_device_vsock<br/>Host-guest comms"]
RUN --> CONSOLE["console_forwarder<br/>Serial I/O"]
RUN --> LOGTEE["log_tee<br/>Log routing"]
RUN --> TOMB["tombstone_receiver<br/>Crash data"]
RUN --> ADB_CONN["adb_connector<br/>ADB over network"]
style CROSVM fill:#e8f5e9,stroke:#2e7d32
style LAUNCH fill:#fff3e0,stroke:#e65100
The assemble_cvd step builds composite disk images from individual partition
images (system, vendor, userdata, boot) and generates the crosvm configuration.
Then run_cvd launches crosvm and all supporting daemons, monitoring their
health and restarting them if they crash.
58.6.17 Vsock: The Glue Between Host and Guest¶
Vsock (Virtual Sockets) is the primary general-purpose communication channel between the Cuttlefish host and guest. Unlike HVC ports (which are point-to-point serial channels), vsock supports arbitrary TCP-like connections with multiplexed ports:
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:755-766
if (instance.vsock_guest_cid() >= 2) {
if (instance.vhost_user_vsock()) {
// vhost-user vsock (separate process)
crosvm_cmd.AddVhostUser("vsock", socket_path);
} else {
// Built-in crosvm vsock
crosvm_cmd.Cmd().AddParameter("--vsock=cid=",
instance.vsock_guest_cid());
}
}
Guest components that use vsock include:
- Camera HAL — streams frames from host webcam
- Light HAL — receives light state changes
- Vehicle HAL — automotive sensor data
- V4L2 streamer — video frame transfer
- Socket proxy — generic socket-to-vsock tunneling
(
common/frontend/socket_vsock_proxy/)
58.6.18 Multi-Instance Support¶
Cuttlefish can run multiple virtual devices simultaneously on a single host, each with its own set of TAP devices, vsock CID, HVC ports, and display:
# Launch 3 concurrent Cuttlefish instances
launch_cvd --num_instances=3
# Each gets:
# Instance 1: CID=3, TAP cvd-mtap-01, port 6520
# Instance 2: CID=4, TAP cvd-mtap-02, port 6521
# Instance 3: CID=5, TAP cvd-mtap-03, port 6522
Instance-specific paths are managed by CuttlefishConfig::InstanceSpecific,
which generates unique socket paths, FIFO paths, and log directories for each
instance. This enables large-scale parallel testing in CI/CD environments.
58.7 Emulator Features¶
58.7.1 Snapshots¶
Snapshots are one of the most powerful features of the Android Emulator. They capture the complete state of the virtual machine -- CPU registers, memory contents, device state, and disk state -- and save it to a file that can be restored later.
graph LR
subgraph "Snapshot Save"
VM_STATE["VM State<br/>(CPU, Memory)"]
DISK_STATE["Disk State<br/>(system, data)"]
DEV_STATE["Device State<br/>(GPU, audio)"]
SNAP_FILE["snapshot.pb<br/>+ ram.bin<br/>+ textures/"]
end
VM_STATE --> SNAP_FILE
DISK_STATE --> SNAP_FILE
DEV_STATE --> SNAP_FILE
subgraph "Snapshot Load"
SNAP_FILE2["snapshot.pb<br/>+ ram.bin<br/>+ textures/"]
RESTORED["Restored VM"]
end
SNAP_FILE2 --> RESTORED
style SNAP_FILE fill:#e8f5e9
style SNAP_FILE2 fill:#e8f5e9
Snapshot types:
-
QuickBoot snapshot -- automatically saved when the emulator is closed and restored when it is next opened. This provides near-instant startup times (typically 2-5 seconds instead of 60+ seconds for a cold boot).
-
Named snapshots -- manually created by the user through the Extended Controls UI or the command line. These are used for saving specific device states (e.g., "logged in", "app installed", "specific screen").
Snapshots are stored in the emulator's AVD (Android Virtual Device) directory,
typically at ~/.android/avd/<name>.avd/snapshots/.
Snapshot contents:
snapshot.pb-- Protobuf metadata (machine configuration, timestamp)ram.bin-- Complete guest RAM contentstextures/-- GPU texture and buffer data<disk>-snapshot.img-- Copy-on-write disk overlays
58.7.2 Screen Recording¶
The emulator supports screen recording in multiple formats:
- WebM (VP8/VP9 video, Vorbis/Opus audio)
- GIF (animated, for quick sharing)
Recording is started through the Extended Controls UI or via the gRPC
control interface. On the Cuttlefish side, the
screen_recording_server host command provides similar functionality.
58.7.3 Location Simulation¶
The emulator provides rich location simulation capabilities:
- Single point -- Set a specific latitude/longitude
- Route playback -- Play back a GPX or KML file along a route
- Speed control -- Adjust playback speed
- Altitude -- Set custom altitude values
These controls feed into the GNSS HAL through the QEMU GPS service.
58.7.4 Battery Simulation¶
The emulator simulates a virtual battery with configurable:
- Charge level (0-100%)
- Charging state (charging, discharging, full, not charging)
- AC/USB power connection status
- Battery health status
- Battery temperature
58.7.5 Multi-Display Support¶
The emulator supports multiple virtual displays, enabling developers to test
multi-screen scenarios. The MultiDisplayProvider package
(device/generic/goldfish/MultiDisplayProvider/) manages display
configuration on the guest side:
# Source: device/generic/goldfish/product/multidisplay.mk
PRODUCT_PACKAGES += MultiDisplayProvider
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
system/lib/libemulator_multidisplay_jni.so \
system/lib64/libemulator_multidisplay_jni.so \
system/priv-app/MultiDisplayProvider/MultiDisplayProvider.apk \
The input device configuration supports up to 11 multi-touch input devices (for 11 virtual displays):
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_COPY_FILES += \
device/generic/goldfish/input/virtio_input_multi_touch_1.idc:... \
device/generic/goldfish/input/virtio_input_multi_touch_2.idc:... \
...
device/generic/goldfish/input/virtio_input_multi_touch_11.idc:...
58.7.6 Foldable Device Simulation¶
The emulator can simulate foldable devices, using the hinge angle sensors defined in the sensors HAL. The goldfish source tree includes specific configurations for foldable form factors:
Pixel Fold configuration:
device/generic/goldfish/pixel_fold/ contains:
device_state_configuration.xml-- defines physical states (folded, unfolded)display_layout_configuration.xml-- display layout for each statedisplay_settings.xml-- display parameterssensor_hinge_angle.xml-- hinge angle sensor mapping
The sensors HAL supports three hinge angle sensors (hinge-angle0, hinge-angle1, hinge-angle2), enabling simulation of devices with multiple hinges:
// Source: device/generic/goldfish/hals/sensors/sensor_list.cpp
{
.sensorHandle = kSensorHandleHingeAngle0,
.name = "Goldfish hinge sensor0 (in degrees)",
.type = SensorType::HINGE_ANGLE,
.maxRange = 360,
.resolution = 1.0,
.flags = SensorFlagBits::DATA_INJECTION |
SensorFlagBits::ON_CHANGE_MODE |
SensorFlagBits::WAKE_UP
},
The foldable emulation uses the existing device state framework. When the user adjusts the hinge angle in the emulator UI, the change flows through:
sequenceDiagram
participant UI as Emulator UI
participant QEMU as QEMU Host
participant SENSOR as Sensors HAL
participant DSS as DeviceStateService
participant WM as WindowManager
UI->>QEMU: Hinge angle changed to 90 deg
QEMU->>SENSOR: "hinge-angle0:90.0"
SENSOR->>DSS: SensorType::HINGE_ANGLE event
DSS->>DSS: Evaluate device state rules
DSS->>WM: Device state: HALF_OPENED
WM->>WM: Reconfigure displays and windows
58.7.7 Wear OS and Rotary Input¶
The emulator supports Wear OS form factors with rotary input simulation. The input device configuration file for rotary input:
This allows developers to test Wear OS apps that respond to the rotating bezel or crown.
58.7.8 Virtual Device Property Configuration¶
The emulator uses a layered property system to configure virtual hardware parameters. Properties can be set at multiple levels:
Boot properties (kernel command line / androidboot):
These are set by the emulator binary and passed to the kernel. They are
available as ro.boot.* properties during early-init.
QEMU properties (qemu-props service): These are fetched from the emulator host via the goldfish-pipe after boot. They configure display density, hardware features, and emulator-specific behavior.
Product properties:
These are baked into the system image at build time. From
device/generic/goldfish/product/generic.mk:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_VENDOR_PROPERTIES += \
ro.control_privapp_permissions=enforce \
ro.crypto.dm_default_key.options_format.version=2 \
ro.crypto.volume.filenames_mode=aes-256-cts \
ro.hardware.power=ranchu \
ro.incremental.enable=yes \
ro.logd.size=1M \
ro.kernel.qemu=1 \
ro.soc.manufacturer=AOSP \
ro.soc.model=ranchu \
ro.surface_flinger.has_HDR_display=false \
ro.surface_flinger.has_wide_color_display=false \
ro.surface_flinger.protected_contents=false \
ro.surface_flinger.supports_background_blur=1 \
ro.surface_flinger.use_color_management=false \
ro.zygote.disable_gl_preload=1 \
debug.sf.vsync_reactor_ignore_present_fences=true \
debug.stagefright.c2inputsurface=-1 \
debug.stagefright.ccodec=4 \
graphics.gpu.profiler.support=false \
persist.sys.zram_enabled=1 \
wifi.direct.interface=p2p-dev-wlan0 \
wifi.interface=wlan0 \
Notable property explanations:
| Property | Value | Purpose |
|---|---|---|
ro.kernel.qemu |
1 |
Framework flag: running in emulator |
ro.soc.model |
ranchu |
Identifies the virtual SoC |
ro.hardware.power |
ranchu |
Power HAL selection |
ro.surface_flinger.has_HDR_display |
false |
No HDR support in virtual display |
ro.surface_flinger.protected_contents |
false |
No DRM-protected content support |
ro.zygote.disable_gl_preload |
1 |
Skip GL preloading (may not have GPU ready at zygote start) |
debug.sf.vsync_reactor_ignore_present_fences |
true |
Simplify VSync handling for virtual displays |
debug.stagefright.ccodec |
4 |
Use Codec2 for media decoding |
persist.sys.zram_enabled |
1 |
Enable zram swap |
58.7.9 Emulator Configuration Files¶
The emulator uses INI-format configuration files stored in the AVD
directory and in the device source tree. From
device/generic/goldfish/data/etc/:
| File | Purpose |
|---|---|
advancedFeatures.ini |
Enable/disable emulator features |
config.ini |
Default hardware configuration |
config.ini.nexus5 |
Nexus 5 emulation config |
config.ini.foldable |
Foldable device config |
config.ini.freeform |
Free-form window mode config |
config.ini.desktop |
Desktop mode config |
config.ini.nexus7tab |
Tablet config |
config.ini.pixeltablet |
Pixel Tablet config |
config.ini.tv |
Android TV config |
The phone product configuration copies these files to the output:
# Source: device/generic/goldfish/product/phone.mk
PRODUCT_COPY_FILES += \
device/generic/goldfish/data/etc/advancedFeatures.ini:advancedFeatures.ini \
device/generic/goldfish/data/etc/config.ini.nexus5:config.ini
58.7.10 Display Configuration Files¶
The emulator supports multiple display layout configurations for different device form factors:
# Source: device/generic/goldfish/product/generic.mk
PRODUCT_COPY_FILES += \
device/generic/goldfish/display_settings_app_compat.xml:\
$(TARGET_COPY_OUT_VENDOR)/etc/display_settings_app_compat.xml \
device/generic/goldfish/display_settings_freeform.xml:\
$(TARGET_COPY_OUT_VENDOR)/etc/display_settings_freeform.xml \
These XML files configure display properties like:
- Display resolution and density
- Window management mode (standard, freeform)
- App compatibility overrides (for apps that do not handle multi-window/foldable correctly)
58.7.11 UWB (Ultra-Wideband) Emulation¶
The emulator includes UWB HAL support through VirtIO console:
# Source: device/generic/goldfish/init/init.ranchu.rc
on property:vendor.qemu.vport.uwb=*
symlink ${vendor.qemu.vport.uwb} /dev/hvc2
start vendor.uwb_hal
service vendor.uwb_hal \
/vendor/bin/hw/android.hardware.uwb-service /dev/hvc2
class hal
user uwb
disabled
58.7.12 Thread Networking¶
Thread network support is provided through a simulated RCP (Radio Co-Processor):
# Source: device/generic/goldfish/product/generic.mk
ifneq ($(EMULATOR_VENDOR_NO_THREADNETWORK), true)
PRODUCT_PACKAGES += \
com.android.hardware.threadnetwork-simulation-rcp
endif
58.8 Try It: Build and Launch a Custom Emulator Image¶
This section walks through building a custom emulator image from source and launching it.
58.8.1 Building the Emulator System Image¶
# Step 1: Set up the build environment
cd /path/to/aosp
source build/envsetup.sh
# Step 2: Choose a target
# For x86_64 emulator (fastest on x86 host):
lunch sdk_phone64_x86_64-userdebug
# For ARM64 emulator:
# lunch sdk_phone64_arm64-userdebug
# Step 3: Build the system image
m -j$(nproc)
The build produces images in $ANDROID_PRODUCT_OUT/:
| Image | Description |
|---|---|
system.img |
System partition |
vendor.img |
Vendor partition |
super.img |
Super partition (containing dynamic partitions) |
userdata.img |
User data partition |
kernel-ranchu |
Kernel binary |
ramdisk.img |
Initial ramdisk |
vendor_boot.img |
Vendor boot image |
58.8.2 Launching with the Emulator¶
# Launch the emulator with the built images
emulator
# Or with specific options:
emulator \
-no-snapshot \ # cold boot (skip QuickBoot)
-gpu host \ # use host GPU acceleration
-memory 4096 \ # 4GB RAM
-cores 4 \ # 4 CPU cores
-no-audio \ # disable audio (faster boot)
-verbose # verbose logging
58.8.3 Building and Launching Cuttlefish¶
# Step 1: Choose Cuttlefish target
lunch aosp_cf_x86_64_phone-userdebug
# Step 2: Build
m -j$(nproc)
# Step 3: Launch (requires cuttlefish-base/user packages installed)
launch_cvd
# Step 4: Access
adb shell
# Step 5: For WebRTC access
# Open https://localhost:8443 in a browser
58.8.4 Customizing the Emulator Image¶
Adding a Custom HAL¶
To add a custom HAL implementation to the emulator, modify the product makefile:
# In your custom product .mk file
$(call inherit-product, device/generic/goldfish/product/phone.mk)
# Add your custom packages
PRODUCT_PACKAGES += \
my.custom.hal-service
# Override properties
PRODUCT_VENDOR_PROPERTIES += \
ro.my.custom.property=value
Modifying Init Behavior¶
Create a custom init RC file and add it to the product:
Modifying SELinux Policy¶
Add custom SELinux policy:
58.8.5 Debugging the Emulator¶
Kernel Logs¶
# View kernel logs from the emulator
adb shell dmesg
# Or through the emulator's logcat forwarding
adb logcat -b kernel
HAL Debugging¶
# Enable verbose logging for sensors HAL
adb shell setprop log.tag.SensorsHAL VERBOSE
# View HAL service logs
adb logcat -s SensorsHAL:V
# Check if HAL services are running
adb shell dumpsys hwservicemanager
GPU Debugging¶
# Check which rendering mode is active
adb shell getprop ro.hardware.egl
# Expected: "emulation" for host GPU, "swiftshader" for software
# Check OpenGL ES version
adb shell getprop ro.opengles.version
# 196610 = OpenGL ES 3.2
# Enable ANGLE debug
adb shell setprop debug.angle.feature_overrides_enabled ...
Network Debugging¶
# Check network configuration inside the guest
adb shell ip addr show
adb shell ip route show
# Test connectivity
adb shell ping -c 3 google.com
# Check WiFi state
adb shell dumpsys wifi
# Port forwarding from host
adb forward tcp:8080 tcp:8080
58.8.6 Performance Tuning¶
CPU and Memory¶
# Launch with more resources
emulator -memory 8192 -cores 8
# Or set in the AVD configuration (config.ini):
hw.ramSize=8192
hw.cpu.ncore=8
GPU Acceleration¶
# Host GPU (fastest, requires compatible host GPU)
emulator -gpu host
# ANGLE on Vulkan (good compatibility)
emulator -gpu angle_indirect
# SwiftShader (software, most compatible, slowest)
emulator -gpu swiftshader_indirect
# Guest rendering (DRM-based, no host GPU needed)
emulator -gpu guest
Disk Performance¶
# Use SSD-backed storage for the AVD directory
# The emulator heavily uses random I/O for disk images
# Increase ZRAM to reduce I/O
# (configured automatically via init.ranchu.rc)
58.8.7 Advanced: Running Multiple Instances¶
Emulator¶
# First instance (default ports)
emulator -avd Phone1 &
# Second instance (automatic port assignment)
emulator -avd Phone2 &
# Verify both are running
adb devices
# List of devices attached
# emulator-5554 device
# emulator-5556 device
Cuttlefish¶
# Launch 3 instances at once
launch_cvd --num_instances=3
# Each instance gets its own:
# - ADB port
# - WebRTC port
# - Console port
58.8.8 Advanced: Custom Kernel¶
# Step 1: Check out the kernel source
repo init -u https://android.googlesource.com/kernel/manifest \
-b common-android-mainline
repo sync
# Step 2: Build the kernel
BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh
# Step 3: Launch emulator with custom kernel
emulator -kernel /path/to/bzImage \
-system $ANDROID_PRODUCT_OUT/system.img \
-vendor $ANDROID_PRODUCT_OUT/vendor.img
58.8.9 Understanding the Build Product Configuration Chain¶
When building an emulator image, the product configuration follows a specific chain of inheritance. Let us trace through the x86_64 phone target:
sdk_phone64_x86_64.mk
-> phone.mk
-> handheld.mk
-> base_handheld.mk
-> generic.mk
-> versions.mk
-> generic_system.mk
-> handheld_system_ext.mk
-> aosp_product.mk
-> base_phone.mk
-> phone_overlays.mk
Each level adds specific functionality:
versions.mk-- Sets version codes and API levelsgeneric.mk-- Core emulator device: HALs, packages, propertiesbase_handheld.mk-- Handheld-specific settings (dalvik heap, etc.)handheld.mk-- Full handheld product with system, system_ext, productbase_phone.mk-- Phone-specific overlaysphone.mk-- Ties it all together, adds config.ini
This layered design means that adding a new emulator product (e.g., a new form factor) requires only creating a thin top-level makefile that inherits from the appropriate base.
58.8.10 Testing HAL Implementations¶
One of the most useful aspects of the emulator for platform developers is the ability to test HAL implementations. Here is a workflow for testing a modified sensors HAL:
# Step 1: Make changes to the sensors HAL
# Edit device/generic/goldfish/hals/sensors/multihal_sensors.cpp
# Step 2: Rebuild just the sensors module
m android.hardware.sensors@2.1-impl.ranchu
# Step 3: Push the updated library
adb root
adb remount
adb push $ANDROID_PRODUCT_OUT/vendor/lib64/hw/\
android.hardware.sensors@2.1-impl.ranchu.so \
/vendor/lib64/hw/
# Step 4: Restart the sensors HAL
adb shell stop
adb shell start
# Step 5: Verify
adb shell dumpsys sensorservice
For the camera HAL:
# Rebuild camera provider
m android.hardware.camera.provider.ranchu
# Push and restart
adb root && adb remount
adb push $ANDROID_PRODUCT_OUT/vendor/bin/hw/\
android.hardware.camera.provider.ranchu \
/vendor/bin/hw/
adb shell stop
adb shell start
# Verify cameras are available
adb shell dumpsys media.camera
58.8.11 Tracing Emulator Communication¶
To debug the communication between guest HALs and the QEMU host, several techniques are available:
1. Logcat filtering:
# Sensor HAL messages
adb logcat -s goldfish:V MultihalSensors:V
# Camera HAL messages
adb logcat -s CameraProvider:V QemuCamera:V
# GNSS HAL messages
adb logcat -s GnssHwConn:V GnssHwListener:V
# Radio HAL messages
adb logcat -s RadioModem:V AtChannel:V
2. Strace on HAL services:
# Find the HAL process
adb shell ps -A | grep sensors
# Attach strace
adb shell strace -p <pid> -e trace=read,write,ioctl -s 256
3. QEMU monitor commands:
From the emulator console (telnet to the console port), you can inspect the state of virtual devices:
58.8.12 Building Slim Emulator Images¶
For CI/CD scenarios where boot time and image size matter, the "slim" emulator variant strips out unnecessary components:
The slim variant (device/generic/goldfish/product/slim_handheld.mk)
inherits from generic.mk directly without the full handheld product
stack, resulting in a smaller system image that boots faster.
58.8.13 Running CTS on the Emulator¶
The emulator is a supported CTS (Compatibility Test Suite) target:
# Step 1: Launch emulator with CTS-compatible settings
emulator -gpu host -memory 4096 -cores 4
# Step 2: Wait for boot to complete
adb wait-for-device
adb shell getprop sys.boot_completed # should return "1"
# Step 3: Run CTS
cd /path/to/cts
./android-cts/tools/cts-tradefed
> run cts --plan CTS
# Or run specific modules
> run cts -m CtsMediaTestCases
> run cts -m CtsSensorTestCases
Some CTS tests require specific sensor values or hardware capabilities that
the emulator provides through its virtual HALs. The data injection support
in the sensors HAL (SensorFlagBits::DATA_INJECTION flag on all sensors)
is specifically designed for CTS compliance.
58.8.14 Emulator Console Commands¶
The emulator exposes a telnet-based console for direct control:
# Connect to the console
telnet localhost 5554
# Useful commands:
# Power simulation
power capacity 50 # set battery to 50%
power status charging # set charging state
# Network simulation
network speed gsm # simulate GSM speeds
network delay gprs # simulate GPRS latency
# SMS simulation
sms send 5551234567 Hello from the console!
# GPS simulation
geo fix -122.084 37.422 # set GPS to Google HQ
# Sensor simulation
sensor set acceleration 0:9.8:0 # set accelerometer
# Fingerprint simulation
finger touch 1 # simulate fingerprint touch
# Snapshot management
avd snapshot save mysnap
avd snapshot load mysnap
avd snapshot list
Summary¶
The Android Emulator is a sophisticated virtualization platform that runs real Android system images on virtual hardware. This chapter covered its layered architecture:
-
QEMU core -- Provides CPU virtualization via KVM (hardware) or TCG (software translation), memory management, and device emulation.
-
Goldfish/Ranchu virtual platform -- A collection of virtual devices including VirtIO (gpu, net, blk, input, console, rng) and the goldfish-pipe for high-bandwidth host-guest communication.
-
HAL implementations -- Nine HAL modules (audio, camera, sensors, GNSS, radio, fingerprint, HWC3, gralloc, plus supporting libraries) that bridge Android's hardware interfaces to the emulator's virtual devices.
-
GPU emulation -- A command-stream architecture where guest GLES/Vulkan calls are serialized, sent to the host via goldfish-pipe, and replayed against the host's actual GPU.
-
Networking -- Virtual router, VirtIO WiFi, port forwarding, and inter-emulator networking.
-
Cuttlefish -- A cloud-oriented alternative that uses the same kernel and VirtIO devices but runs without a desktop GUI, making it ideal for CI/CD and server-side testing.
-
Developer features -- Snapshots for instant restore, multi-display support, foldable simulation, location/battery/telephony simulation, and rich console commands.
The key architectural principle throughout is that the emulator runs real Android -- the same kernel, the same framework, the same system image format. The virtual hardware layer is designed to be transparent to the software above it, so that apps and platform code behave identically whether running on a physical device or in the emulator.
Key Source Files Reference¶
| File | Role |
|---|---|
device/generic/goldfish/AndroidProducts.mk |
Product target definitions |
device/generic/goldfish/board/BoardConfigCommon.mk |
Board configuration |
device/generic/goldfish/product/generic.mk |
Core product configuration |
device/generic/goldfish/product/handheld.mk |
Handheld product configuration |
device/generic/goldfish/init/init.ranchu.rc |
Init script (boot sequence) |
device/generic/goldfish/hals/sensors/multihal_sensors.cpp |
Sensors HAL core |
device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp |
QEMU sensor protocol |
device/generic/goldfish/hals/sensors/sensor_list.cpp |
Sensor definitions |
device/generic/goldfish/hals/camera/CameraProvider.cpp |
Camera provider |
device/generic/goldfish/hals/camera/qemu_channel.cpp |
Camera QEMU pipe communication |
device/generic/goldfish/hals/gnss/GnssHwConn.cpp |
GNSS hardware connection |
device/generic/goldfish/hals/radio/RadioModem.cpp |
Radio modem HAL |
device/generic/goldfish/hals/fingerprint/hal.cpp |
Fingerprint HAL |
device/generic/goldfish/hals/hwc3/HostFrameComposer.cpp |
Host GPU composition |
device/generic/goldfish/hals/hwc3/GuestFrameComposer.cpp |
Guest DRM composition |
device/generic/goldfish/hals/gralloc/allocator.cpp |
Graphics buffer allocator |
device/generic/goldfish/hals/lib/qemud/qemud.cpp |
QEMU multiplexed pipe library |
device/generic/goldfish/qemu-props/qemu-props.cpp |
Boot property service |
device/generic/goldfish/init/init.net.ranchu.sh |
Network initialization |
device/generic/goldfish/sepolicy/vendor/qemu_props.te |
SELinux policy for qemu-props |
device/generic/goldfish/sepolicy/vendor/hal_gnss_default.te |
SELinux policy for GNSS HAL |
device/google/cuttlefish/shared/BoardConfig.mk |
Cuttlefish board config |
device/google/cuttlefish/README.md |
Cuttlefish getting started guide |