第 60 章 模拟器架构

Android 模拟器是 AOSP 生态中最为关键的开发者工具之一。它绝非简单的仿真器,而是一套完整的系统级虚拟机,在经过定制修改的 QEMU 管理程序内运行量产版 Android 系统镜像;在 Linux 平台借助 KVM 实现硬件加速,在其余平台则使用 HAXM / 虚拟机框架完成加速。本章由内到外剖析模拟器:QEMU 执行引擎、Goldfish 与 Ranchu 虚拟硬件平台、衔接虚拟设备与模拟器宿主机的客户侧 HAL 实现、面向云环境的替代方案 Cuttlefish,以及快照、多显示器、可折叠设备仿真等一系列开发者功能,正是这些能力让模拟器成为不可或缺的工具。

支撑模拟器运行的设备树位于 AOSP 源码的device/generic/goldfish/目录。第二个虚拟设备平台 Cuttlefish,代码存放于device/google/cuttlefish/。这两个目录合计包含数十万行 C++ 代码、Shell 脚本、SELinux 策略与 Makefile 配置,这些内容定义了无实体硬件时,"一台 Android 设备" 的具体形态。

60.1 模拟器架构总览

60.1.1 软件栈

Android 模拟器基于 QEMU 的定制分支构建,QEMU 是开源的机器仿真与虚拟化工具。开发者在命令行输入emulator指令时,下面这套分层架构便开始工作:

  • 宿主机(Linux/macOS/Windows)
    • Android 模拟器二进制程序(emulator、qemu‑system‑*)

      • QEMU 核心(TCG 用于软件仿真;KVM/HAXM 用于硬件加速)
        • 虚拟 CPU(vCPU):执行 ARM/x86/RISC‑V 指令
        • 虚拟内存管理(影子页表 / EPT)
        • 中断控制器(ARM 平台为 GICv2/v3;x86 平台为 IOAPIC)
      • Goldfish/Ranchu 虚拟硬件
        • goldfish‑pipe:宿主机与客户机之间的通信通道
        • virtio‑gpu:GPU 透传 / 宿主机渲染
        • virtio‑net:虚拟网络
        • virtio‑input:触摸、键盘、传感器
        • virtio‑blk:块设备仿真
        • virtio‑console:串口 / 控制台端口
      • 模拟器 UI /gRPC 控制接口
        • 外观渲染、扩展控制面板
        • 快照管理
        • 位置 / 电话 / 电池仿真

      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

60.1.2 执行模式

模拟器支持两种基础执行模式:

KVM 加速模式(Linux):客户机代码借助基于内核的虚拟机模块 KVM,直接在宿主机 CPU 上原生运行。该模式性能最优,当客户机与宿主机架构相同时优先启用(x86 客户机运行在 x86 宿主机,ARM 客户机运行在 ARM 宿主机)。启用 KVM 后,绝大多数客户机指令可以接近原生速度执行;只有 IO、页表操作这类特权操作会陷入模拟器完成处理。

软件翻译模式(TCG):QEMU 的微型代码生成器 TCG,实时将客户机指令翻译为宿主机指令。用于架构不匹配的场景,例如在 x86 宿主机运行 ARM 客户机镜像。虽然性能远低于 KVM,但 TCG 实现了跨架构开发。

macOS 平台使用苹果虚拟机框架替代 KVM;Windows 平台则使用英特尔 HAXM(硬件加速执行管理器)或者 Windows 虚拟机平台 WHPX,实现同等能力。

60.1.3 高层数据流

核心认知:模拟器并不是 "模拟" Android,而是真正运行 Android。内核是真实的 Linux 内核,用户空间镜像和实体设备出厂镜像基本一致。模拟器的职责,就是提供这套真实软件所期望的虚拟硬件。

60.1.4 关键源码目录

目录 用途
device/generic/goldfish/ Goldfish 虚拟设备定义、HAL、初始化脚本
device/google/cuttlefish/ Cuttlefish 虚拟设备定义
device/generic/goldfish/hals/ 硬件抽象层 HAL 实现
device/generic/goldfish/init/ 模拟器启动的 init RC 脚本
device/generic/goldfish/sepolicy/ 模拟器专属域的 SELinux 策略
device/generic/goldfish/board/ 板级配置(区分不同架构)
device/generic/goldfish/product/ 产品配置 Makefile

60.1.5 产品配置

模拟器定义了若干产品目标,在device/generic/goldfish/AndroidProducts.mk中声明:

Go 复制代码
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

这些目标覆盖 x86_64、ARM64、RISC‑V 架构;同时包含手机、平板、精简版等不同设备形态,以及标准 /minigbm 两类图形后端。

60.2 Goldfish 设备平台

"Goldfish" 是 Android 模拟器初代虚拟硬件平台。该名称指代 QEMU 向客户机内核呈现的一整套虚拟设备(定时器、中断控制器、IO 总线等)。随着迭代,绝大多数原始的自定义 Goldfish 设备,已经在现代 Ranchu 平台替换为标准 virtio 设备,但 Goldfish 这个名称依旧保留在 AOSP 源码树中。

60.2.1 产品配置继承层级

Goldfish 产品配置采用分层继承结构:

根配置文件为device/generic/goldfish/product/generic.mk,它定义模拟器设备的核心配置,节选如下:

复制代码
PRODUCT_VENDOR_PROPERTIES += \
    ro.hardware.power=ranchu \
    ro.kernel.qemu=1 \
    ro.soc.manufacturer=AOSP \
    ro.soc.model=ranchu

属性ro.kernel.qemu=1是框架用来识别当前运行在模拟器内部的标志性标志。SoC 型号标识为ranchu,即模拟器虚拟平台的现代代号。

60.2.2 板级配置

板级配置存放于device/generic/goldfish/board/,每种架构变体拥有独立子目录:

目录 架构
board/emu64x/ x86_64
board/emu64a/ ARM64
board/emu64xr/ x86_64 + RISC‑V(原生桥接)
board/emu64ar/ ARM64 + RISC‑V(原生桥接)
board/emu64x16k/ x86_64,16KB 页大小
board/emu64a16k/ ARM64,16KB 页大小

全部架构均继承board/BoardConfigCommon.mk,该文件定义通用板级配置,关键片段:

复制代码
TARGET_BOOTLOADER_BOARD_NAME := goldfish_$(TARGET_ARCH)

# 编译OpenGL ES仿真的客户机与宿主机库
BUILD_EMULATOR_OPENGL := true
BUILD_QEMU_IMAGES := true
USE_OPENGL_RENDERER := true

# 模拟器不支持稀疏镜像格式
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true

# 模拟器属于非A/B设备
AB_OTA_UPDATER := none

# 模拟器需要super.img
BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true

# 8G + 8M
BOARD_SUPER_PARTITION_SIZE ?= 8598323200
BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions

关键要点:

  1. 模拟器使用 super 分区与动态分区,对齐现代实体设备;
  2. 属于非 A/B 设备,没有双分区无缝 OTA 能力;
  3. 同时编译客户机侧、宿主机侧的 OpenGL ES 仿真库;
  4. WiFi 子系统基于 NL80211 与内核模块mac80211_hwsim,仿真无线网络接口。

60.2.3 HAL 实现

Goldfish 设备平台的核心是整套 HAL(硬件抽象层)实现,代码位于device/generic/goldfish/hals/,承担 Android 硬件接口与模拟器虚拟设备之间的桥接工作。

60.2.3.1 音频 HAL

路径:device/generic/goldfish/hals/audio/

音频 HAL 实现android.hardware.audio@7.1接口,借助 TinyALSA 访问 QEMU 提供的虚拟声卡。实现包含如下文件:

  • primary_device.cpp:主音频设备,处理音量、麦克风静音、流创建
  • stream_out.cpp / stream_in.cpp:输出流、输入流实现
  • talsa.cpp:TinyALSA 封装层
  • device_port_sink.cpp / device_port_source.cpp:音频路由端口抽象

primary_device.cpp节选:

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;
}

音频延迟参数在产品 Makefile 配置:

Go 复制代码
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

80ms 的宿主机延迟高于实体设备(实体设备目标 5‑20ms),原因是音频数据需要经过 QEMU 虚拟声卡再流转到宿主机音频子系统。

60.2.3.2 摄像头 HAL

路径:device/generic/goldfish/hals/camera/

摄像头 HAL 实现 AIDL Camera Provider 接口,支持多种摄像头数据源:

  1. QEMU 摄像头(BaseQemuCameraGasQemuCameraMinigbmQemuCamera):对接宿主机摄像头或者虚拟场景,通过 QEMU 管道机制通信;
  2. 虚拟旋转摄像头(FakeRotatingCamera):合成测试用摄像头,输出旋转 3D 图案。

与 QEMU 宿主机通信基于qemu_channel抽象,qemu_channel.cpp节选:

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;
   }
   // ... 解析ok/ko应答 ...
}

Camera Provider 使用带device@1.1/internal/前缀的 ID 体系,CameraProvider.cpp节选:

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;
}
60.2.3.3 传感器 HAL

路径:device/generic/goldfish/hals/sensors/

传感器 HAL 是模拟器硬件虚拟化极具代表性的模块,基于 QEMU 传感器协议实现android.hardware.sensors@2.1

完整仿真传感器列表定义在sensor_list.cpp

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",
   "low‑latency‑off‑body‑detect",
};

合计 20 个虚拟传感器,包含加速度计、陀螺仪、磁场、方向、温度、接近光、亮度、气压、湿度、铰链角度、心率、手腕倾斜姿态等。

通信协议 :传感器 HAL 通过 QEMU 管道使用简单文本协议通信。multihal_sensors_qemu.cpp节选:

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;
   }
}

协议指令:

  • list‑sensors:查询宿主机支持的传感器,返回位掩码
  • set:<sensor_name>:<0|1>:启用 / 关闭指定传感器
  • set‑delay:<ms>:设置上报周期,单位毫秒
  • time:<ns>:客户机时钟同步

传感器事件解析 :宿主机下发传感器文本消息,例如acceleration:9.8:0.0:0.1,由parseQemuSensorEventLocked完成解析。

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;
       }
   }
   // ...陀螺仪、磁场、接近光、亮度等同类处理逻辑
}

架构上使用专用监听线程qemuSensorListenerThread与批处理线程batchThread,专门处理连续上报模式传感器。

60.2.3.4 GNSS HAL

路径:device/generic/goldfish/hals/gnss/

GNSS 全球导航卫星系统 HAL,提供虚拟 GPS 数据。核心类GnssHwConn打开指向gps服务的 QEMU 管道。GnssHwConn.cpp节选:

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();
}

工作线程借助 epoll 同时监听 QEMU 设备文件描述符(接收宿主机 GPS 数据)与命令 fd(接收关闭信号)。当模拟器扩展控制面板下发 GPS 定位点,数据依次流经 QEMU 的 gps 服务、管道、GnssHwListener解析 NMEA 语句,最终送入 Android LocationManager。

GNSS 设备节点由 init 脚本创建软链接:

cpp 复制代码
on property:vendor.qemu.vport.gnss=*
   symlink ${vendor.qemu.vport.gnss} /dev/gnss0

对应的 SELinux 策略赋予 GNSS HAL 访问 vsock 套接字权限:

复制代码
vndbinder_use(hal_gnss_default);
allow hal_gnss_default self:vsock_socket create_socket_perms_no_ioctl;
60.2.3.5 无线通信(电话)HAL

路径:device/generic/goldfish/hals/radio/

无线通信 HAL 实现全套电话 AIDL 接口:

  • RadioModem:调制解调器控制(开关机、IMEI、无线能力)
  • RadioSim:SIM 卡管理
  • RadioNetwork:网络注册、信号强度、小区信息
  • RadioData:数据通话与链路建立
  • RadioVoice:语音通话
  • RadioMessaging:短信
  • RadioIms:IP 多媒体子系统 IMS

无线通信 HAL 通过通道向调制解调器仿真器发送 AT 指令。RadioModem.cpp节选:

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();
}

AT 指令接口基于AtChannel抽象,采用请求‑应答模式:

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();
}

产品 Makefile 配置支持 5G NR、LTE、TD‑SCDMA、CDMA、EVDO、GSM、WCDMA 多种网络制式:

cpp 复制代码
# NR 5G, LTE, TD‑SCDMA, CDMA, EVDO, GSM and WCDMA
PRODUCT_VENDOR_PROPERTIES += ro.telephony.default_network=33
60.2.3.6 指纹 HAL

路径:device/generic/goldfish/hals/fingerprint/

指纹 HAL 实现 AIDL IFingerprint服务,实现相对简单。hal.cpp节选:

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;
   // ...
}

模拟器扩展控制面板提供虚拟指纹扫描器,经由该 HAL 触发认证事件。

60.2.3.7 硬件合成器 HWC3 HAL

路径:device/generic/goldfish/hals/hwc3/

硬件合成器 HAL 是模拟器所有 HAL 中实现最复杂的模块,提供两种合成模式:

  1. HostFrameComposer(宿主机帧合成器):把合成工作交给宿主机 GPU,实现硬件加速渲染;
  2. GuestFrameComposer(客户机帧合成器):客户机侧基于 DRM 与 libyuv 完成合成,作为宿主机渲染不可用时的回退方案。

宿主机帧合成器使用gfxstream协议向模拟器进程传递合成指令。HostFrameComposer.cpp节选:

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;
   }
}

客户机帧合成器回退实现,GuestFrameComposer.cpp节选:

cpp 复制代码
#include "Drm.h"
#include "Layer.h"
#include "DisplayFinder.h"

std::array<std::int8_t, 16> ToLibyuvColorMatrix(
       const std::array<float, 16>& in) {
   // 将HAL色彩矩阵转换为libyuv格式
   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] * 66.0f + 0.5f));
           out[indexOut] = static_cast<std::int8_t>(clampedValue);
       }
   }
   return out;
}

HWC3 完整实现了基于 DRM 的显示管理,封装 plane、CRTC、connector 抽象;相关源码包含Drm.cppDrmClient.cppDrmConnector.cppDrmCrtc.cppDrmDisplay.cppDrmPlane.cppDrmSwapchain.cppDrmAtomicRequest.cppDrmBuffer.cppDrmEventListener.cppDrmMode.cpp

60.2.3.8 Gralloc 图形内存分配器 HAL

路径:device/generic/goldfish/hals/gralloc/

图形内存分配器负责分配可供 CPU、GPU 访问的缓冲区。allocator.cpp节选:

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;
           // ...
       }
       // ...
   }
};

核心设计:CPU 缓冲区借助GoldfishAddressSpaceBlock在客户机内存分配;GPU 缓冲区在宿主机侧表示为颜色缓冲区。缓冲区需要 GPU 访问时,分配器通过渲染控制编码器,在宿主机创建颜色缓冲区。

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);
   }
}

分配器支持大量像素格式:RGBA_8888、RGB_565、RGBA_FP16、RGBA_1010102、YV12、YCBCR_420_888、YCBCR_P010。Mapper 库后缀为ranchu

cpp 复制代码
ndk::ScopedAStatus getIMapperLibrarySuffix(std::string* outResult) override {
   *outResult = "ranchu";
   return ndk::ScopedAStatus::ok();
}

60.2.4 Init 初始化脚本

模拟器启动流程由device/generic/goldfish/init/目录下 init 脚本控制,主脚本为init.ranchu.rc,关键片段:

cpp 复制代码
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

模拟器专属功能服务:

  • qemu‑props:读取模拟器宿主机的启动属性,并设置为 Android 系统属性
  • ranchu‑adb‑setup:配置模拟器 ADB
  • ranchu‑net:配置网络(VirtIO WiFi、模拟器间互通)
  • ranchu‑setup:系统启动完成后执行后置配置
  • goldfish‑logcat:将 logcat 输出通过 virtio 控制台/dev/hvc1转发到宿主机
  • bt_vhci_forwarder:转发蓝牙 HCI 流量

渲染子系统默认参数在early‑init阶段配置:

复制代码
setprop ro.hardware.egl ${ro.boot.hardwareegl:-emulation}
# 默认skiagl:skia使用GLES完成渲染
setprop debug.hwui.renderer ${ro.boot.debug.hwui.renderer:-skiagl}
# 默认skiaglthreaded
setprop debug.renderengine.backend \
   ${ro.boot.debug.renderengine.backend:-skiaglthreaded}

60.2.5 SELinux 策略

模拟器在device/generic/goldfish/sepolicy/定义自定义 SELinux 策略。vendor 策略目录包含约 60 个策略文件,覆盖全部模拟器专属域与服务。

关键策略文件:

文件 用途
qemu_props.te qemu‑props属性设置服务策略
hal_sensors_default.te 传感器 HAL 访问 vsock 套接字权限
hal_gnss_default.te GNSS HAL 访问 vsock 与 binder 节点权限
hal_radio_default.te 无线通信 HAL 访问调制解调器仿真器权限
hal_camera_default.te 摄像头 HAL 访问 QEMU 管道权限
hal_graphics_composer_default.te HWC3 访问 DRM、GPU 权限
hal_graphics_allocator_default.te Gralloc 缓冲区分配策略
goldfish_setup.te 模拟器初始化脚本策略
goldfish_ip.te 网络配置策略

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;

qemu_props域被授予设置特定分类属性,以及通过 vsock(VirtIO socket)通信的权限;vsock 是客户机内核与 QEMU 宿主机最主要的通信通道。

60.2.6 完整软件包清单

模拟器产品配置会引入大量软件包,device/generic/goldfish/product/generic.mk中分类如下:

核心图形栈

复制代码
PRODUCT_PACKAGES += \
   vulkan.ranchu \
   libandroidemu \
   libOpenglCodecCommon \
   libOpenglSystemCommon \
   android.hardware.graphics.composer3‑service.ranchu

vulkan.ranchu提供模拟器 Vulkan 可安装客户端驱动 ICD;libandroidemu与 OpenGL 编解码 / 系统库实现 GPU 仿真流水线的客户机侧逻辑。

OpenGL ES 仿真库

复制代码
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

库成对分工:

  • lib*_emulation:客户机侧 EGL/GLES 实现,拦截 API 调用
  • lib*_enc:编码器,将 GLES/Vulkan 命令序列化为二进制流,传输到宿主机
  • lib*_angle:基于 ANGLE、由 Vulkan 后端驱动的 GLES 实现

媒体编解码器

复制代码
PRODUCT_PACKAGES += \
   android.hardware.media.c2@1.0‑service‑goldfish \
   libcodec2_goldfish_vp8dec \
   libcodec2_goldfish_vp9dec \
   libcodec2_goldfish_avcdec \
   libcodec2_goldfish_hevcdec

Goldfish 媒体编解码器基于 Codec2 框架,视频解码交由 QEMU 宿主机完成,让模拟器内部也可以硬件加速视频播放。

合规 HAL(最简 Hello‑World 实现)

复制代码
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

这部分是最小实现,仅满足 CTS 兼容性测试套件要求,不会执行真实硬件操作,只提供框架需要的 AIDL 服务接口。

条件编译包选择

产品 Makefile 通过编译标志条件引入组件:

编译标志 默认值 置为 true 效果
EMULATOR_DISABLE_RADIO false 禁用电话 HAL
EMULATOR_VENDOR_NO_BIOMETRICS false 禁用指纹 HAL
EMULATOR_VENDOR_NO_GNSS false 禁用 GNSS HAL
EMULATOR_VENDOR_NO_SENSORS false 禁用传感器 HAL
EMULATOR_VENDOR_NO_CAMERA false 禁用摄像头 HAL
EMULATOR_VENDOR_NO_SOUND false 禁用音频 HAL
EMULATOR_VENDOR_NO_UWB false 禁用 UWB 超宽带 HAL
EMULATOR_VENDOR_NO_THREADNETWORK false 禁用 Thread 网络
EMULATOR_VENDOR_NO_REBOOT_ESCROW false 禁用重启托管功能

借助这些标志,可以编译出裁剪后的极简模拟器镜像,用于特定专项测试。

60.3 虚拟硬件

60.3.1 Goldfish‑Pipe:宿主机‑客户机通信

goldfish‑pipe 是 Android 客户机与 QEMU 宿主机之间的核心通信机制。它属于虚拟设备,提供双向字节流接口,类似 Unix 管道,但跨越虚拟机边界。

客户机侧 API :通过libqemu_pipe库调用接口:

  • qemu_pipe_open_ns(namespace, name, flags):打开指向特定宿主机服务的命名管道
  • qemu_pipe_write_fully(fd, data, size):向管道写入完整数据
  • qemu_pipe_read_fully(fd, data, size):从管道读取完整数据

qemud层在原始管道之上实现多路复用,单条管道连接之上可以打开多条逻辑通道。qemud.cpp节选:

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) { // 使用二进制编码
       uint32_t length32be = htonl(size | (1U << 31));
       memcpy(header, &length32be, 4);
   } else { // 使用十六进制字符编码
       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;
}

协议采用长度前缀帧格式

  • 消息小于 64KB:4 位十六进制字符作为长度前缀,例如001a
  • 消息大于等于 64KB:4 字节大端二进制长度,最高比特置 1。

60.3.2 传感器 HAL 线程模型

传感器 HAL 拥有一套复杂多线程架构。头文件multihal_sensors.h暴露内部结构:

cpp 复制代码
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;

       // 变化上报型传感器(宿主机不维护状态)
       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;
   };

   // 批处理事件
   struct BatchEventRef {
       int64_t  timestamp = -1;
       int      sensorHandle = -1;
       int      generation = 0;

       bool operator<(const BatchEventRef &rhs) const {
           // 注意:让top()返回时间戳最小元素
           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;
};

一共三类线程:

  1. 主线程:处理 SensorService 发起的 HIDL/AIDL 调用(activate、batch、flush、injectSensorData_2_1);
  2. 传感器监听线程 qemuSensorListenerThread:从 QEMU 传输通道读取传感器数据,分发事件;使用 epoll 多路复用传输 fd 与命令 fd;
  3. 批处理线程 batchThread:实现连续模式传感器批上报;基于时间戳优先队列,决定下一条传感器事件的投递时机。

epoll 监听线程实现节选multihal_sensors_epoll.cpp

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;
               }
           }
       }
   }
}

kCMD_RESTART:批上报周期变更,需要重新配置传输通道时发送; kCMD_QUIT:HAL 关闭阶段发送。该设计支持监听线程重启,不需要销毁整个 HAL 实例。

60.3.3 显示设备发现与 VSync 垂直同步

HWC3 HAL 通过渲染控制编码器查询 QEMU 宿主机,完成显示设备发现。DisplayFinder.cpp节选:

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;  // 默认60Hz
   }
   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));
       }
   }
   // ...
}

显示发现逻辑向宿主机查询:

  1. 分辨率宽高:FB_WIDTHFB_HEIGHT
  2. DPI 每英寸点数:FB_XDPIFB_YDPI
  3. 刷新率:来自 boot 属性ro.boot.qemu.vsync

当宿主机支持多显示配置模式,会枚举全部可用配置,通过 HWC3 接口上报给 SurfaceFlinger。

60.3.4 音频写线程

音频 HAL 输出流使用独立写线程,搭配 FMQ 快速消息队列,实现与 AudioFlinger 低延迟通信。stream_out.cpp节选:

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);
   }
};

FMQ 机制允许 AudioFlinger 写入音频数据,无需 Binder 往返;EventFlag借助共享内存,在 AudioFlinger 进程与 HAL 服务进程之间完成轻量信号通知。

60.3.5 唤醒锁管理

模拟器初始化脚本通过唤醒锁管理电源状态。init.setup.ranchu.sh节选:

cpp 复制代码
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

默认情况下,模拟器持有永久唤醒锁emulator_wake_lock,阻止客户机进入深度休眠。对开发调试十分关键:休眠后的模拟器将失去响应。设置ro.boot.qemu.allowsuspend=1可以放开休眠,用于电源管理行为专项测试。

60.3.6 QEMU 属性服务

qemu‑props服务源码device/generic/goldfish/qemu‑props/qemu‑props.cpp,模拟器启动早期就会运行。作用是读取宿主机侧属性,设置为 Android 系统属性。

cpp 复制代码
// 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;
}

设置属性完成后,服务进入心跳循环,周期性向 QemuMiscPipe 服务发送 "heartbeat" 心跳消息。以此让模拟器主机检测虚拟机客户机是否存活且响应正常:

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();
    }
    // ...
}

源码路径:device/generic/goldfish/qemu‑props/qemu‑props.cpp

60.3.7 虚拟传感器

虚拟传感器(详见 60.2.3.3 节)由模拟器扩展控制界面驱动。当用户操作传感器控件(倾斜虚拟设备、修改接近传感器数值、调节光照等级),模拟器主机通过 QEMU 管道发送文本格式传感器事件。

传感器数据流:

传感器 HAL 会给未校准传感器数据增加校准噪声,以此通过 CTS 兼容性测试:

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);
       // ...
   }
}

源码路径:device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp

60.3.8 虚拟 GPS

GPS 仿真基于 GNSS HAL 实现(60.2.3.4 节)。模拟器支持如下能力:

  • 固定 GPS 坐标(通过扩展控制面板配置)
  • GPS 路线回放(GPX/KML 文件)
  • NMEA 语句注入

GPS 服务在 QEMU 主机注册为gps qemud 服务。客户机侧GnssHwListener类解析收到的 NMEA 数据,并分发至 Android 位置框架。

60.3.9 虚拟相机

相机子系统支持多种虚拟相机后端:

  1. 主机摄像头透传 :模拟器采集主机摄像头帧,通过 QEMU camera服务发送给客户机。
  2. 虚拟场景:3D 渲染环境,跟随虚拟设备姿态传感器输出画面。
  3. 伪旋转相机 :合成测试图案,对应源码类FakeRotatingCamera,位于device/generic/goldfish/hals/camera/

相机数据传输复用 qemud 协议,采用查询‑应答交互模式:

复制代码
客户机 → 主机: "list"          (列举可用相机)
主机 → 客户机: "ok:<camera_list>"
客户机 → 主机: "connect:<id>"  (连接指定相机)
主机 → 客户机: "ok"
客户机 → 主机: "start:<params>" (开启图像采集)
主机 → 客户机: "ok"
主机 → 客户机: <frame_data>    (原始帧数据)
cpp 复制代码
Guest -> Host: "list"       (on the "camera" factory channel)
Host -> Guest: "ok:<camera_list>"
Guest opens qemud channel "camera:name=<camera>"
Guest -> Host: "connect"    (connect to the camera)
Host -> Guest: "ok"
Guest -> Host: "start"      (start capture)
Host -> Guest: "ok"
Host -> Guest: <frame_data> (raw frame data)
Guest -> Host: "stop", then "disconnect" when done

60.3.10 虚拟电话

电话子系统内置完整调制解调器模拟器,通过 AT 命令与无线 HAL 通信。模拟器支持:

  • 语音通话(仿真通话状态机)
  • 短信收发
  • 数据连接
  • SIM 卡仿真(ICC 配置文件存放在data/misc/modem_simulator/
  • 多种无线接入技术(5G NR、LTE、GSM 等)

SIM 卡配置文件预编译拷贝:

复制代码
# 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 \

60.3.11 GPU 仿真

GPU 仿真属于模拟器内部架构最复杂模块之一。系统支持三类渲染主体:主机侧渲染、客户机侧渲染、goldfish‑pipe 通道。

客户机镜像内置相关库:

复制代码
# 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

客户机侧 EGL/GLES 库会将 OpenGL ES 指令序列化为二进制数据流,经由 goldfish‑pipe 发送至主机。模拟器主机解码指令,调用本机 GPU 驱动重放绘图命令。

当主机 GPU 不可用时(无头 CI 服务器、SSH 远程会话),SwiftShader 提供完全运行于 CPU 的 Vulkan 软件实现。

ANGLE(Almost Native Graphics Layer Engine)在 Vulkan 接口之上封装 OpenGL ES。适合主机支持 Vulkan,但缺少原生 OpenGL 驱动的场景,新版 macOS 就属于这类平台。

图形渲染模式通过启动属性配置:

复制代码
# 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}

gralloc HAL 借助 render control 编码器创建主机端 GPU 资源(颜色缓冲区,见 60.2.3.8),实现高效零拷贝渲染:客户机完成帧合成后,直接由模拟器窗口进行显示输出。

60.4 模拟器网络

60.4.1 网络架构

模拟器实现一套虚拟网络,给客户机提供互联网访问,同时将虚拟机与主机物理网络隔离。默认使用 QEMU 用户态网络协议栈 SLIRP;高级场景可选用 TAP 网络模式。

60.4.2 默认 IP 地址分配

每个模拟器实例分配独立 IP 网段:

组件 地址
虚拟路由器 / 网关 10.0.2.1
主机回环别名 10.0.2.2
DNS 服务器 10.0.2.3
客户机 eth0 10.0.2.15(DHCP 分配)

60.4.3 VirtIO WiFi

新版模拟器使用 VirtIO WiFi 替代传统 eth0 网卡。网络脚本device/generic/goldfish/init/init.net.ranchu.sh完成相关处理:

复制代码
# 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

开启 VirtIO WiFi 后,mac80211_hwsim内核模块创建仿真 WiFi 射频。wpa_supplicant服务管理该虚拟网卡:

复制代码
# 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

VirtIO WiFi 条件触发配置:

复制代码
# Source: device/generic/goldfish/init/init.ranchu.rc
on post-fs-data && property:ro.boot.qemu.virtiowifi=1
    start ranchu-net

60.4.4 端口转发与 ADB 连接

模拟器支持主机与客户机之间 TCP、UDP 端口转发。ADB 依靠端口转发完成通信:

  • 控制台端口:实例 1 使用 5554,实例 2 使用 5556,以此类推
  • ADB 端口:实例 1 使用 5555,实例 2 使用 5557,以此类推

端口转发在模拟器控制台配置:

复制代码
# 将主机8080端口转发到客户机80端口
redir add tcp:8080:80

# 将主机5000端口转发到客户机5000端口
redir add tcp:5000:5000

客户机内部 ADB 守护进程监听固定端口;模拟器自动配置转发规则,adb devices即可识别模拟器设备。

60.4.5 多模拟器实例互通网络

网络脚本提供第二块网卡eth1,用于模拟器之间通信:

复制代码
# 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

多模拟器需要互相通信(多设备场景测试)时,可以配置共享网络,每个实例在 eth1 上分配独立 IP。

60.4.6 蓝牙网络

蓝牙基于 VirtIO 控制台设备仿真。init 脚本创建蓝牙设备符号链接:

复制代码
# 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

bt_vhci_forwarder服务打通 VirtIO 控制台设备与蓝牙 VHCI(虚拟主机控制器接口)驱动,客户机就可以使用模拟器蓝牙协议栈。

60.5 Ranchu 与 Goldfish 内核

60.5.1 历史背景

模拟器经历两代主要内核:

  1. Goldfish 内核(遗留版本):经过定制修改的 Linux 内核,包含专门适配早期模拟器虚拟硬件的驱动(goldfish_timer、goldfish_fb、goldfish_audio、goldfish_battery 等)。
  2. Ranchu 内核(现代版本):标准 GKI 通用内核镜像,使用标准 VirtIO 设备,不再使用定制 Goldfish 硬件。Ranchu 本身是金鱼品种,命名体现演进继承关系。

60.5.2 VirtIO 设备迁移

从定制 Goldfish 硬件迁移到标准 VirtIO 设备是一次重大架构升级。

goldfish‑pipe予以保留:它承担 VirtIO 无法覆盖的能力,提供高带宽低延迟通道,用于客户机 HAL 与主机服务之间传输序列化 GPU 指令以及其他大块数据。

60.5.3 内核模块配置

现代 Ranchu 内核采用可加载 VirtIO 内核模块。Cuttlefish 板级配置使用同一套内核,下面列出 ramdisk 阶段所需模块:

复制代码
# 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 \

以上模块必须在第一阶段 init 加载,保证系统可以正常启动。后续加载模块列表:

  • virtio_blk.ko:块设备仿真
  • virtio_console.ko:串口控制台、虚拟端口
  • virtio_pci.ko:VirtIO 的 PCI 传输后端
  • vmw_vsock_virtio_transport.ko:客户机‑主机 vsock 通信
  • mac80211_hwsim.ko:WiFi 仿真
  • cfg80211.komac80211.ko:无线网络协议栈

60.5.4 内核版本选择

Android17 中,Cuttlefish 内核版本不再写死固定值。device/google/cuttlefish/shared/BoardConfig.mk定义默认版本,各产品可以单独选择;部分目标从 release‑config 变量读取内核版本。

复制代码
# Source: device/google/cuttlefish/shared/BoardConfig.mk
DEFAULT_TARGET_KERNEL_USE := 6.12

ifneq (,$(findstring cf_gwear_arm,$(PRODUCT_NAME)))
TARGET_KERNEL_USE ?= 6.6
else ifeq (true,$(CLOCKWORK_EMULATOR_PRODUCT))
TARGET_KERNEL_USE ?= 6.1
else ifneq (,$(findstring x86_tv,$(PRODUCT_NAME)))
TARGET_KERNEL_USE ?= 6.1
else ifneq (,$(filter cf_x86_64_desktop,$(PRODUCT_NAME)))
TARGET_KERNEL_USE ?= $(RELEASE_KERNEL_CUTTLEFISH_X86_64_VERSION)
TARGET_KERNEL_DIR ?= $(RELEASE_KERNEL_CUTTLEFISH_X86_64_DIR)
else ifneq (,$(filter cf_arm64_desktop,$(PRODUCT_NAME)))
TARGET_KERNEL_USE ?= $(RELEASE_KERNEL_CUTTLEFISH_ARM64_VERSION)
TARGET_KERNEL_DIR ?= $(RELEASE_KERNEL_CUTTLEFISH_ARM64_DIR)
else
TARGET_KERNEL_USE ?= $(DEFAULT_TARGET_KERNEL_USE)
endif

通用手机目标默认内核版本 6.12;部分形态锁定长期支持内核:Wear OS 使用 6.6,clockwork 模拟器、x86 TV 使用 6.1。桌面目标比较特殊,内核版本、目录取自RELEASE_KERNEL_CUTTLEFISH_*变量,内核与模块来自独立桌面专用代码树,不再使用公共预编译包。

复制代码
# Source: device/google/cuttlefish/shared/BoardConfig.mk
ifneq (,$(filter cf_x86_64_desktop cf_arm64_desktop,$(PRODUCT_NAME)))
SYSTEM_DLKM_SRC ?= device/google/desktop/cuttlefish‑$(TARGET_KERNEL_ARCH)‑kernels/$(TARGET_KERNEL_USE)/$(TARGET_KERNEL_DIR)/system_dlkm
KERNEL_MODULES_PATH ?= device/google/desktop/cuttlefish‑$(TARGET_KERNEL_ARCH)‑kernels/$(TARGET_KERNEL_USE)/$(TARGET_KERNEL_DIR)/vendor_dlkm
else
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))
endif

TARGET_KERNEL_PATH ?= $(SYSTEM_DLKM_SRC)/kernel‑$(TARGET_KERNEL_USE)

除桌面目标之外,预编译内核存放在kernel/prebuilts/common‑modules/virtual‑device/目录存放专门为虚拟设备编译的内核模块。

60.5.5 ZRAM 与内存配置

Ranchu 内核开启 zram 内存压缩:

复制代码
# 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}

zram 压缩选用 LZ4,压缩解压速度快。page‑cluster=0设置内核每次读取一个 swap 页,zram 没有磁盘寻道开销,该参数为最优配置。

60.6 Cuttlefish:面向云环境的替代虚拟设备

60.6.1 什么是 Cuttlefish

Cuttlefish 是一套可配置 Android 虚拟设备,运行于云环境,不需要物理显示器、音频硬件或者任何硬件专属基础设施。Goldfish/Ranchu 面向 Android Studio 模拟器(GUI 桌面程序);Cuttlefish 面向服务器侧场景:CI/CD 持续集成、自动化测试、云游戏、远程机器开发。

源码路径:device/google/cuttlefish/

60.6.2 架构对比

特性 Goldfish/Ranchu Cuttlefish
主要使用场景 桌面开发 云环境 / CI 自动化
显示输出 桌面窗口 WebRTC 或者 VNC
音频 主机音频输出 虚拟音频
GPU 主机 GPU 透传 virtio‑gpu / SwiftShader
网络 用户态 SLIRP TAP / 网桥模式
多实例 多进程运行 launch_cvd --num_instances
OTA 更新 不支持 支持 A/B 更新
快照 QEMU 快照 非主打能力
设备形态 手机、平板、折叠、Wear、TV 手机、折叠、平板 PC、TV、车载、Wear、桌面
支持架构 x86_64、ARM64、RISC‑V x86_64、ARM64、RISC‑V

60.6.3 Cuttlefish 设备编译目标

device/google/cuttlefish/AndroidProducts.mk枚举编译目标。产品命名格式:aosp_cf_<arch>_<formfactor>,每个产品指向对应vsoc_*设备目录下精简aosp_cf.mkvsoc含义:Virtual System on Chip 虚拟片上系统。

Android17 源码中设备目录:

目录 架构 / 变体
vsoc_x86_64/ x86_64(兼容 32 位)
vsoc_x86_64_only/ 纯 x86_64,仅 64 位
vsoc_x86_64_pgagnostic/ x86_64,页大小无关(4KB/16KB)
vsoc_x86_64_minidroid/ 极简 x86_64 版本
vsoc_x86_64_host/ x86_64 主机侧编译
vsoc_arm64/ ARM64(兼容 32 位)
vsoc_arm64_only/ 纯 ARM64
vsoc_arm64_pgagnostic/ ARM64,页大小无关
vsoc_arm64_minidroid/ 极简 ARM64
vsoc_arm/ vsoc_arm_minidroid/ 32 位 ARM
vsoc_riscv64/ RISC‑V 64 位
vsoc_riscv64_minidroid/ 极简 RISC‑V

设备形态在以上目录基础上扩展。Android17 已经远超手机平板,产品列表覆盖手机、折叠、平板 PC、TV、Wear、车载、新增桌面目标。

产品名 设备目录 说明
aosp_cf_x86_64_phone vsoc_x86_64/phone/ CI 标准参考目标
aosp_cf_x86_64_foldable vsoc_x86_64_only/phone/ 叠加 aosp_cf_foldable.mk 配置
aosp_cf_x86_64_pc vsoc_x86_64_only/pc/ 大屏平板布局
aosp_cf_x86_64_tv vsoc_x86_64_only/tv/ Android TV
aosp_cf_x86_64_wear vsoc_x86_64_only/wear/ Wear OS
aosp_cf_x86_64_auto (+ auto_md, auto_mdnd, auto_dd, auto_portrait...) vsoc_x86_64_only/auto*/ 车载,多显示变体
aosp_cf_x86_64_desktop vsoc_x86_64_only/desktop/ Android17 新增桌面目标
aosp_cf_arm64_phone / aosp_cf_arm64_auto vsoc_arm64* ARM 平台对应版本
aosp_cf_riscv64_phone / _wear / _slim vsoc_riscv64* RISC‑V 平台
Android17 桌面目标

aosp_cf_x86_64_desktop是 Android17 新增重要目标。产品配置文件device/google/cuttlefish/vsoc_x86_64_only/desktop/aosp_cf.mk继承桌面专属厂商配置device/google/cuttlefish/shared/desktop/common_x86.mkaosp_device_vendor.mk;设置PRODUCT_MODEL := Cuttlefish AOSP x86_64 Desktop。两点区别于手持设备:

复制代码
# Source: device/google/cuttlefish/vsoc_x86_64_only/desktop/aosp_cf.mk
PRODUCT_NAME := aosp_cf_x86_64_desktop
PRODUCT_DEVICE := vsoc_x86_64_only

# Ika uses ndk‑translation only.
AL_BINARY_TRANSLATION_MODE := ndk_translation_only

# ARC/Auto/Desktop - don't use compressed apks.
UNCOMPRESS_CHROME_WEBVIEW = true

桌面目标内核取自独立路径,不再使用公共kernel/prebuilts/,桌面 Android 维护独立内核分支。

60.6.4 主机侧工具集

Cuttlefish 大量主机工具存放于device/google/cuttlefish/host/commands/,编译产出cvd‑host_package.tar.gz,与客户机镜像配合运行。

工具 用途
launch_cvd 启动虚拟设备,编译输出cvd_internal_start,建立符号链接
stop_cvd 停止虚拟设备
run_cvd 核心虚拟机运行时,管理 crosvm 与后台守护进程
assemble_cvd 组装磁盘镜像,生成 VMM 虚拟机监控器配置
status_cvd / restart_cvd 查询、重启运行实例
cvd_env gRPC 环境管理
process_sandboxer 使用 seccomp 沙箱隔离主机守护进程(Android17 新增)
console_forwarder 串口控制台转发
kernel_log_monitor 内核日志监控
log_tee 日志复制转发
logcat_receiver 接收来自客户机 logcat 日志
modem_simulator 电话调制解调器仿真
gnss_grpc_proxy gRPC 方式 GNSS 位置数据代理
display 显示管理
screen_recording_server 录屏服务
record_cvd 录屏工具
secure_env 安全环境(KeyMint、Gatekeeper、TPM)
sensors_simulator 传感器仿真
vhost_user_input vhost‑user 输入设备后端
casimir_control_server NFC 控制
jcardsim Java Card 仿真,用于安全元件 /eSIM
health 设备健康监控
host_bugreport 抓取 bugreport
metrics 指标采集统计
snapshot_util_cvd 快照管理
powerbtn_cvd 模拟电源按键
powerwash_cvd 模拟恢复出厂设置
cvd_send_sms 注入短信
cvd_update_location / cvd_import_locations 注入位置信息

注意:主机工具发生重大迁移:编排前端 cvd、Debian 软件包不再维护在 AOSP device/google/cuttlefish目录,迁移至独立仓库github.com/google/android‑cuttlefishdevice/google/cuttlefish/README.md给出指引。Android17 不再包含旧版 acloud 启动器;本地启动使用 host 包内launch_cvd或者外部仓库 cvd 命令。

60.6.5 板级配置差异

Cuttlefish 板级配置device/google/cuttlefish/shared/BoardConfig.mk相比 Goldfish 有多处关键改动:

支持 A/B OTA 升级

复制代码
# Source: device/google/cuttlefish/shared/BoardConfig.mk
AB_OTA_UPDATER := true

启用更多动态分区

复制代码
# Source: device/google/cuttlefish/shared/BoardConfig.mk
BOARD_SUPER_PARTITION_SIZE := 8589934592  # 8GB
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

独立 ODM、vendor_dlkm 分区

复制代码
# 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

内核命令行自定义

复制代码
# 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

Cuttlefish 特有内核参数说明:

  • binder.impl=rust:使用 Rust 实现 binder 驱动
  • cma=0:关闭连续内存分配器,虚拟机环境不需要
  • panic=-1:内核 panic 后立刻重启

60.6.6 Cuttlefish 快速上手

来自官方device/google/cuttlefish/README.md

复制代码
# 1. 确认CPU支持KVM虚拟化
grep -c -w "vmx\|svm" /proc/cpuinfo

# 2. 安装主机依赖包
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. 从ci.android.com下载镜像

# 4. 启动实例
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. 浏览器WebRTC访问 https://localhost:8443

# 6. ADB调试
./bin/adb -e shell

# 7. 停止虚拟机
HOME=$PWD ./bin/stop_cvd

60.6.7 Cuttlefish VirtIO 模块依赖

Cuttlefish 板级配置完整展示虚拟设备所需 VirtIO 软件栈。模块分为 ramdisk(第一阶段 init)、vendor 分区(第二阶段 init)。

ramdisk 模块(启动必须):

复制代码
# 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 \

必须第一阶段 init 加载的原因:

  • virtio‑gpu.ko:显示输出依赖
  • virtio_net.ko:初始化阶段网络访问
  • virtio_input.ko:输入事件
  • nd_virtio.ko:VirtIO NUMA 距离支持
  • virtio‑rng.ko:随机数生成,密码组件初始化依赖

传输层模块:

复制代码
# 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

VirtIO PCI 传输模块是 x86 平台 QEMU 环境所有 VirtIO 设备基础。ARM 平台替换为virtio_mmio.ko

WiFi 相关模块(mac80211 协议栈)

复制代码
# 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)

mac80211_hwsim软件模拟 WiFi 射频。第一阶段 init 加载该模块时参数mac80211_hwsim.radios=0,不会立刻创建射频;射频实例后续由mac80211_create_radios工具生成。

60.6.8 Cuttlefish 对比 Goldfish 架构差异

核心差异:Goldfish 整体是单一 QEMU 进程,虚拟机、显示、设备仿真全部在内部完成。Cuttlefish 采用微服务架构,虚拟机管理、显示、调制解调器、GNSS、日志等功能各自独立进程。模块化强,调试友好,但部署复杂度更高。

60.6.9 选型参考

场景 推荐方案
Android Studio 开发 Goldfish/Ranchu 模拟器
需要 UI 本地调试 Goldfish/Ranchu 模拟器
CI/CD 自动化测试 Cuttlefish
云端开发 Cuttlefish
性能测试 Cuttlefish(行为更确定)
CTS/VTS 兼容性测试 Cuttlefish(官方主要参考)
多实例并发测试 Cuttlefish
大量快照工作流 Goldfish/Ranchu 模拟器
折叠设备 UI 交互测试 两者均可,Goldfish UI 更完善
车载 / TV / Wear OS Cuttlefish,设备形态覆盖更全

Cuttlefish 逐步成为 AOSP 首要虚拟参考设备。谷歌内部持续使用它做自动化测试。平台开发人员如果不需要 GUI 交互界面,优先选用 Cuttlefish。

60.6.10 Crosvm 设备架构

Cuttlefish 默认 VMM 虚拟机监控器为crosvm ,使用 Rust 语言开发,最初为 Chrome OS 开发。虚拟机管理代码device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp(共 1076 行)组装 crosvm 命令行,填入全部 VirtIO 设备参数。

VirtIO 设备映射

客户机所有 IO 设备均基于 PCI 总线 VirtIO:

PCI 槽位分配
cpp 复制代码
// Source: device/google/cuttlefish/host/libs/vm_manager/vm_manager.h:79‑91
static constexpr int kMaxDisks = 3;
static const int kDefaultNumBootDevices = 2;
static constexpr const int kNetPciDeviceNum = 1;   // Network on PCI slot 1
static constexpr const int kGpuPciSlotNum = 2;     // GPU on PCI slot 2

网卡占用 PCI 槽位 1 下面子地址:

接口 PCI 地址 TAP 设备 用途
Mobile 00:01.1 cvd‑mtap‑NN 蜂窝数据仿真
Ethernet 00:01.2 cvd‑etap‑NN 有线网络
WiFi 00:01.3 cvd‑wtap‑NN 无线网络(可选)

60.6.11 Vhost‑User 设备模型

Cuttlefish 使用 vhost‑user 协议,设备后端运行在独立主机进程,不在 crosvm 进程内部。隔离性更好,可以单独重启设备后端;后端可以使用不同语言实现(输入设备 Rust、音频服务 C++)。

cpp 复制代码
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_builder.h:71‑72
void AddVhostUser(const std::string& type, const std::string& socket_path,
                  int max_queue_size = 256);

支持 vhost‑user 设备:

类型 后端进程 套接字路径 用途
gpu vhost‑user‑gpu gpu_socket_path() 图形渲染
input vhost_user_input(Rust) keyboard_socket_path () 等 输入事件
vsock vhost‑device‑vsock vhost_user_vsock_path() 主机‑客户机通信
block vhost‑user‑block disk socket 存储磁盘(仅 disk2)
mac80211‑hwsim WiFi simulator hwsim socket WiFi 射频仿真

virtqueue 默认队列大小 256 项,数值必须是 2 的整数次幂。

60.6.12 HVC 端口映射

Cuttlefish 使用 Hypervisor Virtual Console (HVC) 端口打通客户机 HAL 与主机守护进程。客户机内设备节点为/dev/hvcN。Android17 一共有 20 个端口/dev/hvc0 ~ /dev/hvc19。端口编号在crosvm_manager.cpp固定分配;即使功能关闭也会配置空 sink 设备,保证 PCI 设备 ID 稳定不变。

客户机设备 主机端点 用途
/dev/hvc0 kernel_log_pipe 内核控制台输出
/dev/hvc1 console_pipe Android 串口控制台
/dev/hvc2 logcat_pipe 系统日志转发 logcat
/dev/hvc3 secure_env daemon C++ KeyMaster HAL
/dev/hvc4 secure_env daemon 锁屏 Gatekeeper 校验
/dev/hvc5 root_canal simulator 蓝牙 HCI 通道
/dev/hvc6 gnss_grpc_proxy GPS/GNSS 位置数据
/dev/hvc7 Location daemon 注入位置修正数据
/dev/hvc8 Trusty integration 安全确认弹窗
/dev/hvc9 UWB daemon 超宽带测距
/dev/hvc10 OEMLock daemon OEM bootloader 解锁
/dev/hvc11 secure_env daemon Rust KeyMint HAL
/dev/hvc12 NFC daemon NFC 仿真
/dev/hvc13 sink 预留未使用
/dev/hvc14 MCU daemon 微控制器控制
/dev/hvc15 MCU daemon 微控制器串口
/dev/hvc16 TPM daemon Ti50 TPM FIFO 命令通道
/dev/hvc17 Java Card simulator eSIM / 安全元件
/dev/hvc18 sensors_simulator 传感器控制命令通道
/dev/hvc19 sensors_simulator 传感器采样数据流通道

对比旧版本重要变更:原先单一传感器端口拆分,控制通道/dev/hvc18,数据通道/dev/hvc19/dev/hvc13空置保留。

每个 HVC 端口主机侧后端为管道或者 Unix 套接字。builder 提供 4 个辅助接口,全部接收 FIFO / 套接字路径字符串:

复制代码
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_builder.h:42‑45
void AddHvcSink();                                            // null device (unused port)
void AddHvcReadOnly(const std::string& output, bool console = false); // one‑way (kernel logs)
void AddHvcReadWrite(const std::string& output, const std::string& input); // bidirectional
void AddHvcSocket(const std::string& socket);                 // Unix socket

60.6.13 GPU 管线与显示模式

Cuttlefish 支持多种 GPU 渲染模式,通过--gpu_mode参数配置。Android17 assemble_cvd 全部可选值集合:{auto, custom, drm_virgl, gfxstream, gfxstream_guest_angle, gfxstream_guest_angle_host_swiftshader, gfxstream_guest_angle_host_lavapipe, guest_swiftshader}

模式 说明
auto 自动检测主机 GPU,选择最优模式
gfxstream gfxstream 协议实现主机 GPU 透传
gfxstream_guest_angle 客户机侧 ANGLE,gfxstream 传输调用主机 GPU
gfxstream_guest_angle_host_swiftshader 客户机 ANGLE,主机 SwiftShader 软件渲染
gfxstream_guest_angle_host_lavapipe 客户机 ANGLE,主机 Mesa lavapipe
drm_virgl Virgl3D;OpenGL 指令经由 virtio‑gpu DRM 转发
guest_swiftshader 完全客户机内部 SwiftShader Vulkan 软件渲染
custom 调用方提供完整 GPU 设备配置

Virtio‑GPU 工作线程运行在进程内还是独立vhost‑user‑gpu后端是另一个独立开关,由--gpu_vhost_user_mode={auto, on, off}控制。

显示架构

Wayland 合成器接收渲染帧,两个输出流向:

  1. WebRTC:浏览器流媒体,云场景使用
  2. 本地显示:直接输出主机屏幕
cpp 复制代码
// 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

60.6.14 网络架构

TAP 设备与网桥配置

Cuttlefish 在主机创建 TAP 网络设备,把客户机 virtio‑网卡桥接到主机网络。

cpp 复制代码
// 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 仿真

Cuttlefish 支持两种 WiFi 仿真模式:

  1. TAP 网桥:简单网络桥接,不实现 WiFi 协议行为

  2. mac80211_hwsim:内核模块仿真 802.11 射频;虚拟机内部完整支持 WiFi 扫描、关联、WPA 鉴权。

    // Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp
    // WiFi via mac80211_hwsim when config.virtio_mac80211_hwsim() is true

vhost‑net 加速

开启后网络包处理从 crosvm 用户态下移到主机内核,大幅提升网络吞吐。

复制代码
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:596‑598
if (instance.vhost_net()) {
   crosvm_cmd.Cmd().AddParameter("--vhost‑net");
}

60.6.15 客户机 HAL

客户机 HAL 层位于device/google/cuttlefish/guest/hals/,对接 Android 上层 HAL 接口,底层通过 virtio 设备、vsock、HVC 串口和主机守护进程通信。Android17 目录结构:

复制代码
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
├── gralloc/         # Graphics buffer allocation
├── health/          # Battery/charge monitoring
├── hostapd/         # WiFi access‑point daemon
├── identity/        # Identity credential HAL
├── ir/              # Consumer IR
├── keymint/         # HVC -> secure_env (KeyMint)
├── light/           # vsock -> light control (Rust)
├── nfc/             # HVC -> NFC daemon
├── npu/             # NPU scheduling HAL (Rust, new in 17)
├── oemlock/         # HVC -> OEM unlock
├── secure_element/  # eSIM / secure chip access
├── vehicle/         # vsock -> automotive VHAL
├── virtio_media/    # virtio‑media V4L2 camera/codec provider
└── vulkan/          # Graphics support

移除模块:不再有独立ril/目录,电话功能走 HVC 端口对接主机modem_simulator;不再有sensors/目录,传感器桥接通过主机sensors_simulator使用/dev/hvc18/dev/hvc19。Android17 新增 NPU 调度 HAL、virtio_media 组件。

客户机 HAL 一般读写/dev/hvcN virtio‑console,或者建立 vsock 连接访问主机对应服务。向上给 Framework 提供的 HAL 接口与真实硬件完全一致;上层无感知虚拟化。

示例:Android17 Rust 实现 NPU 调度 HAL NPU HAL 是 Android17 新增组件,Rust 语言实现android.hardware.npu调度接口,注册服务IScheduling/default,Cuttlefish 对外暴露 NPU 能力,用于 NPU 框架路径测试。

复制代码
// Source: device/google/cuttlefish/guest/hals/npu/main.rs
//! This implements the NPU Scheduling Service for Cuttlefish.
use android_hardware_npu::aidl::android::hardware::npu::IScheduling::{
    BnScheduling, IScheduling};
const LOG_TAG: &str = "android.hardware.npu";

xml 服务配置:

复制代码
<!-- Source: device/google/cuttlefish/guest/hals/npu/android.hardware.npu‑service.xml -->
<fqname>IScheduling/default</fqname>

示例:Vsock 实现 Camera HAL Camera HAL 通过 vsock 接收主机 MJPEG/H264 视频帧,向 CameraService 输出标准 Camera2 HAL 接口。

复制代码
// 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

示例:Rust 实现 Light HAL 注册ILightsbinder 服务,实现通知 LED、背光等指示灯控制接口。

复制代码
// Source: device/google/cuttlefish/guest/hals/light/main.rs
//! This implements the Lights Service for Cuttlefish.
use android_hardware_light::aidl::android::hardware::light::ILights::{
    BnLights, ILights};
mod lights;
use lights::LightsService;

60.6.16 主机微服务编排

Cuttlefish 由launch_cvdrun_cvd协调一组主机进程。device/google/cuttlefish/host/commands/包含约 45 个子工具目录。运行实例按需启动组件:VMM 虚拟机管理器、显示、调制解调器、GNSS、传感器、安全、日志、输入守护进程。

assemble_cvd 把 system、vendor、userdata、boot 分区镜像组装合成磁盘镜像,生成 crosvm 全部配置。run_cvd 启动 crosvm 与所有后台守护进程,监控进程健康,守护进程异常崩溃会自动重启。

60.6.17 Vsock:打通主机‑客户机的核心通道

Vsock (Virtual Sockets) 是 Cuttlefish 主机‑客户机之间通用通信通道。对比 HVC 点对点串口,vsock 支持多路类似 TCP 的连接,可以复用多个端口。

cpp 复制代码
// Source: device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp:756‑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());
   }
}

使用 vsock 的虚拟机组件包括:

  • Camera HAL:从宿主机摄像头传输画面帧流
  • Light HAL:接收光线状态变更事件
  • Vehicle HAL:车载传感器数据
  • V4L2 流处理器:视频帧数据传输
  • Socket 代理:通用 socket 转 vsock 隧道转发(代码路径:common/frontend/socket_vsock_proxy/

60.6.18 多实例支持

Cuttlefish 支持在同一台宿主机上同时运行多台虚拟设备,每个实例拥有独立的 TAP 网卡设备、vsock CID、HVC 端口以及显示输出。

复制代码
# 启动3个并行的Cuttlefish实例
launch_cvd --num_instances=3

各实例分配资源示例:

  • 实例 1:CID=3,TAP 网卡 cvd‑mtap‑01,端口 6520
  • 实例 2:CID=4,TAP 网卡 cvd‑mtap‑02,端口 6521
  • 实例 3:CID=5,TAP 网卡 cvd‑mtap‑03,端口 6522

实例专属路径由 CuttlefishConfig::InstanceSpecific 管理,它会为每一个实例生成独立的 socket 路径、FIFO 管道路径与日志目录,以此支撑 CI/CD 环境下的大规模并行测试。

60.7 模拟器功能

60.7.1 快照(Snapshots)

快照是 Android 模拟器最核心的能力之一。它完整捕获虚拟机全部运行状态:CPU 寄存器、内存数据、外设状态、磁盘状态,并保存为文件,后续可以一键恢复运行现场。

快照分类

  1. QuickBoot(快速启动快照) 模拟器关闭时自动保存,下次启动自动恢复。开机耗时可缩短至 2‑5 秒,省去冷启动 60 秒以上的开机时间。
  2. 命名快照(Named snapshots) 用户通过扩展控制面板或者命令行手动创建,用于保存特定测试状态,例如 "已登录账号"、"预装待测应用"、"固定测试界面"。

快照文件存放在 AVD(Android 虚拟设备)目录下,默认路径: ~/.android/avd/<设备名>.avd/snapshots/

快照内部文件构成

  • snapshot.pb:Protobuf 格式元数据(虚拟机硬件配置、时间戳)
  • ram.bin:完整虚拟机内存镜像
  • textures.bin:GPU 纹理与显存缓冲区数据
  • <disk>-snapshot.img:写时复制(Copy‑on‑Write)磁盘差分镜像

60.7.2 屏幕录制

模拟器支持多种格式录屏:

  • WebM (VP8/VP9 video, Vorbis/Opus audio)
  • GIF (animated, for quick sharing)

录屏可通过扩展控制界面或 gRPC 控制接口开启。Cuttlefish 侧提供主机命令 screen_recording_server 实现同类能力。

60.7.3 位置模拟

模拟器提供完善的位置模拟能力:

  • 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

控制指令经由 QEMU GPS 服务下发到 GNSS HAL。

60.7.4 电池模拟

模拟器可模拟虚拟电池,可配置项:

  • Charge level (0-100%)
  • Charging state (charging, discharging, full, not charging)
  • AC/USB power connection status
  • Battery health status
  • Battery temperature

60.7.5 多显示支持

模拟器支持多块虚拟显示屏,方便开发者做多屏场景测试。客户机侧由 MultiDisplayProvider 包管理显示配置:

复制代码
# 来源: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

输入设备配置最多支持 11 路多点触控输入设备,对应 11 块虚拟显示屏:

复制代码
# 来源: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:...

60.7.6 折叠屏设备模拟

模拟器借助传感器 HAL 定义的铰链角度传感器实现折叠设备模拟。goldfish 源码树包含折叠形态专用配置。

Pixel Fold 配置位于 device/generic/goldfish/pixel_fold/,包含:

  • device_state_configuration.xml -- defines physical states (folded, unfolded)
  • display_layout_configuration.xml -- display layout for each state
  • display_settings.xml -- display parameters
  • sensor_hinge_angle.xml -- hinge angle sensor mapping

传感器 HAL 支持三路铰链角度传感器(hinge‑angle0、hinge‑angle1、hinge‑angle2),可模拟多铰链设备。

复制代码
// 来源: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
},

折叠屏模拟复用现有设备状态框架。用户在模拟器界面修改铰链角度,数据流:

60.7.7 Wear OS 与旋转输入

模拟器支持 Wear OS 设备形态,可模拟旋转输入。旋转输入设备配置文件:

复制代码
# 在 generic.mk 中被引用
device/generic/goldfish/input/virtio_input_rotary.idc

开发者可用来测试响应旋转表圈 / 表冠的 Wear OS 应用。

60.7.8 虚拟设备属性配置

模拟器使用分层属性系统配置虚拟硬件参数。属性分为多层:

启动属性(内核命令行 /androidboot) 由模拟器程序设置并传递给内核,在 early‑init 阶段即可以 ro.boot.* 系统属性的形式读取。

**QEMU 属性(qemu‑props 服务)**设备启动完成后,通过 goldfish‑pipe 通道从模拟器宿主机拉取。用于配置屏幕密度、硬件特性以及模拟器专属行为。

产品属性 编译阶段直接固化在系统镜像中,定义路径示例:device/generic/goldfish/product/generic.mk

复制代码
# 来源: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

关键属性说明:

属性 用途
ro.kernel.qemu 1 框架标识:运行于模拟器
ro.soc.model ranchu 标识虚拟 SoC
ro.hardware.power ranchu 选定 Power HAL 实现
ro.surface_flinger.has_HDR_display false 虚拟显示器不支持 HDR
ro.surface_flinger.protected_contents false 不支持 DRM 受保护内容
ro.zygote.disable_gl_preload 1 关闭 Zygote 阶段 GL 预加载,避免 GPU 未就绪
debug.sf.vsync_reactor_ignore_present_fences true 简化虚拟显示器 VSync 处理逻辑
debug.stagefright.ccodec 4 使用 Codec2 做媒体解码
persist.sys.zram_enabled 1 启用 zram 交换分区

60.7.9 模拟器配置文件

模拟器使用 INI 格式配置文件,存放于 AVD 目录以及设备源码树。来自 device/generic/goldfish/data/etc/

文件 作用
advancedFeatures.ini 开启 / 关闭模拟器各项功能
config.ini 默认硬件配置
config.ini.nexus5 Nexus5 模拟器配置
config.ini.foldable 折叠屏设备配置
config.ini.freeform 自由窗口模式配置
config.ini.desktop 桌面模式配置
config.ini.nexus7tab 平板配置
config.ini.pixeltablet Pixel 平板配置
config.ini.tv Android TV 配置

phone 产品配置将这些文件复制到编译输出:

复制代码
# 来源: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

60.7.10 显示配置文件

模拟器针对不同设备形态提供多套显示布局配置:

复制代码
# 来源: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

XML 文件配置内容包括:

  • 显示分辨率与像素密度
  • 窗口管理模式(标准模式、自由窗体模式)
  • 应用兼容覆盖配置(用于适配无法正常支持多窗口 / 折叠屏的应用)

60.7.11 UWB(超宽带)模拟

模拟器通过 VirtIO console 提供 UWB HAL 支持:

复制代码
# 来源: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

60.7.12 Thread 网络

Thread 网络能力由模拟 RCP(无线协处理器)提供:

复制代码
# 来源:device/generic/goldfish/product/generic.mk
ifneq ($(EMULATOR_VENDOR_NO_THREADNETWORK), true)
PRODUCT_PACKAGES += \
    com.android.hardware.threadnetwork‑simulation‑rcp
endif

60.8 Android 17 Cuttlefish 变更

每个版本大部分虚拟设备改动落在 Cuttlefish,Android17 也不例外。本节整理平台开发者需要关注的变更:新增桌面产品目标、客户机侧 VKMS 显示控制器、沙盒化宿主机进程模型、全新 NPU HAL,宿主机启动工具链移出 AOSP 源码树。

60.8.1 桌面产品目标

Android17 在 Cuttlefish 产品目录新增 aosp_cf_x86_64_desktop(配套 ARM64 版本)。和手持设备目标不同,桌面产品继承桌面专属厂商栈,内核取自独立内核树。它作为 Android 桌面形态的虚拟参考设备,验证大屏窗口管理、ndk_translation_only 模式二进制翻译、未压缩 Chrome WebView。该产品在 device/google/cuttlefish/AndroidProducts.mk 注册,和已有的手机、折叠屏、TV、Wear,以及多套车载目标并列。

60.8.2 vkms_controller:客户机侧虚拟显示管理

旧版 Cuttlefish 将虚拟显示配置逻辑分散在宿主机命令与零散客户机脚本。Android17 将逻辑收敛到独立客户机二进制程序 vkms_controller,直接操作内核 VKMS(Virtual Kernel Mode Setting)ConfigFS 接口。宿主机命令行变为无状态薄代理,等价执行 adb shell vkms_controller ...

cpp 复制代码
// 来源:device/google/cuttlefish/guest/commands/vkms_controller/main.cpp
namespace cuttlefish {
namespace vkms_controller {

constexpr std::string_view kUsage = R"(
Usage: vkms_controller <command> [options]

A guest‑side utility for Virtual Kernel Mode Setting (VKMS).
It manages virtual displays by interacting directly with the kernel's VKMS
ConfigFS interface.

Commands:
 setup
 hotplug
 reset
 list‑presets
 list‑displays
)";

该程序负责映射 VKMS ConfigFS 连接器编号到 Android SurfaceFlinger 显示 ID,状态持久化路径 /data/vendor/vkms。支持指定 EDID、图层定义显示屏(setup)、热插拔连接器(hotplug)、列举内置显示器预设(list‑presets)、查询当前显示拓扑(list‑displays)。通过 device/google/cuttlefish/shared/device.mk 安装:

复制代码
# 来源:device/google/cuttlefish/shared/device.mk
PRODUCT_PACKAGES += \
    checkpoint_gc \
    vkms_controller

测试脚本或宿主机命令行修改显示配置数据流:

60.8.3 process_sandboxer:宿主机守护进程沙箱

Cuttlefish 长期存在一个问题:宿主机运行大量辅助守护进程(虚拟机管理器、调制解调器模拟器、GNSS 代理、secure_env 等),权限宽泛。Android17 新增 device/google/cuttlefish/host/commands/process_sandboxer/,借助 sandboxed‑api /sandbox2 库 seccomp 沙箱封装宿主机进程。

cpp 复制代码
// 来源:device/google/cuttlefish/host/commands/process_sandboxer/main.cpp
#include <sandboxed_api/util/fileops.h>
#include "host/commands/process_sandboxer/policies.h"
#include "host/commands/process_sandboxer/sandbox_manager.h"

namespace cuttlefish::process_sandboxer {
absl::Status ProcessSandboxerMain(int argc, char** argv) {

沙箱为每个可执行程序维护独立策略;policies 目录为每个被沙箱的宿主机工具(run_cvd.cpp、assemble_cvd.cpp、gnss_grpc_proxy.cpp、secure_env.cpp、logcat_receiver.cpp、modem_simulator.cpp、socket_vsock_proxy.cpp 等)提供策略源码,同时包含通用基线策略 baseline.cpp 和逃逸出口 no_policy.cpp。每条策略声明该守护进程允许调用的系统调用与访问文件路径;即便辅助进程被攻破,也无法超出声明访问范围。配套工具 sandboxer_proxy,允许沙箱内进程向管理器申请特权操作。

这是平台内进程隔离在宿主机侧的对应实现:不再把 launcher 完整权限赋予每一个 Cuttlefish 辅助程序,每个程序运行在最小权限策略约束之下。

60.8.4 NPU 调度 HAL

Android17 的 Cuttlefish 新增使用 Rust 编写的客户机 NPU(神经网络处理单元)HAL,路径 device/google/cuttlefish/guest/hals/npu/。注册 android.hardware.npu 调度服务,平台 NPU 业务流程拥有虚拟硬件可供测试。该 HAL 打包为独立 APEX 服务,配套 VINTF 片段声明 IScheduling/default。本版本两大新增客户机 HAL 目录:NPU HAL 与 virtio_media。

60.8.5 宿主机启动工具链迁移

Android17 开发者获取、运行 Cuttlefish 宿主机工具的方式发生变更。总控 cvd 前端程序以及 Debian 软件包不再保留在 AOSP 的 device/google/cuttlefish,迁移至独立仓库 github.com/google/android‑cuttlefish。README 指引开发者跳转外部仓库查阅文档。

实际本地启动分为两条路线: 本源码树编译出来宿主机包依旧提供 launch_cvdstop_cvdlaunch_cvd 软链接指向 cvd_internal_start,编译来源 device/google/cuttlefish/host/commands/start/,对应 README 快速上手流程。

复制代码
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
# ......
HOME=$PWD ./bin/stop_cvd

完整生命周期管理器 cvd、可安装 Debian 包(cuttlefish‑base、cuttlefish‑user)来自外部仓库。Android17 源码树不再包含旧文档提到的 acloud launcher。

60.9 实操:编译并启动自定义模拟器镜像

60.9.1 编译模拟器系统镜像

复制代码
# 步骤1:初始化编译环境
cd /path/to/aosp
source build/envsetup.sh

# 步骤2:选择编译目标
# x86_64模拟器(x86宿主机性能最好)
lunch sdk_phone64_x86_64‑userdebug

# ARM64模拟器
# lunch sdk_phone64_arm64‑userdebug

# 步骤3:编译系统镜像
m -j$(nproc)

编译产物输出在 $ANDROID_PRODUCT_OUT/

镜像 说明
system.img system 分区
vendor.img vendor 分区
super.img super 分区,承载动态分区
userdata.img 用户数据分区
kernel‑ranchu 内核二进制
ramdisk.img 初始 ramdisk
vendor_boot.img vendor boot 镜像

60.9.2 使用模拟器启动镜像

复制代码
# 使用编译好镜像直接启动模拟器
emulator

# 带参数启动示例
emulator \
    -no‑snapshot \          # 冷启动,跳过快速启动快照
    -gpu host \             # 使用宿主机GPU加速
    -memory 4096 \          # 内存4GB
    -cores 4 \              # 4核CPU
    -no‑audio \             # 关闭音频,加快启动
    -verbose                # 输出详细日志

60.9.3 编译并运行 Cuttlefish

复制代码
# 步骤1:选择Cuttlefish编译目标
lunch aosp_cf_x86_64_phone‑userdebug

# 步骤2:编译
m -j$(nproc)

# 步骤3:启动(需要预先安装 cuttlefish‑base / cuttlefish‑user 软件包)
launch_cvd

# 步骤4:进入设备shell
adb shell

# WebRTC访问:浏览器打开 https://localhost:8443

60.9.4 定制模拟器镜像

添加自定义 HAL

在自定义产品 mk 文件:

复制代码
# 自定义产品 .mk
$(call inherit‑product, device/generic/goldfish/product/phone.mk)

# 添加自定义包
PRODUCT_PACKAGES += \
    my.custom.hal‑service

# 覆盖属性
PRODUCT_VENDOR_PROPERTIES += \
    ro.my.custom.property=value
修改 init 行为

新建自定义 init rc,加入产品拷贝规则:

复制代码
PRODUCT_COPY_FILES += \
    my/custom/init.custom.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/init.custom.rc
修改 SELinux 策略
复制代码
BOARD_VENDOR_SEPOLICY_DIRS += my/custom/sepolicy

60.9.5 模拟器调试

内核日志
复制代码
# 查看模拟器内核日志
adb shell dmesg

# 或者读取kernel缓冲区logcat
adb logcat -b kernel
HAL 调试
复制代码
# 打开传感器HAL详细日志
adb shell setprop log.tag.SensorsHAL VERBOSE

# 查看HAL服务日志
adb logcat -s SensorsHAL:V

# 查看HAL服务运行状态
adb shell dumpsys hwservicemanager
GPU 调试
复制代码
# 查询当前渲染后端
adb shell getprop ro.hardware.egl
# host GPU返回 emulation;软件渲染返回 swiftshader

# 查询OpenGL ES版本
adb shell getprop ro.opengles.version
# 196610 = OpenGL ES 3.2

# 开启ANGLE调试
adb shell setprop debug.angle.feature_overrides_enabled ...
网络调试
复制代码
# 查看客户机网络配置
adb shell ip addr show
adb shell ip route show

# 连通性测试
adb shell ping -c 3 google.com

# 查看WiFi状态
adb shell dumpsys wifi

# adb端口转发
adb forward tcp:8080 tcp:8080

60.9.6 性能调优

CPU 与内存
复制代码
# 启动时指定更大资源
emulator -memory 8192 -cores 8

# 或者修改AVD配置 config.ini
hw.ramSize=8192
hw.cpu.ncore=8
GPU 加速
复制代码
# host GPU(速度最快,要求宿主机GPU兼容)
emulator -gpu host

# ANGLE Vulkan模式,兼容性好
emulator -gpu angle_indirect

# SwiftShader纯软件渲染,兼容性最好,速度最慢
emulator -gpu swiftshader_indirect

# 客户机渲染,基于DRM,不依赖宿主机GPU
emulator -gpu guest
磁盘性能
cpp 复制代码
# 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)

60.9.7 进阶:运行多实例

标准模拟器
复制代码
# 实例1使用默认端口
emulator -avd Phone1 &

# 实例2自动分配端口
emulator -avd Phone2 &

# 查看全部设备
adb devices
# emulator‑5554   device
# emulator‑5556   device
Cuttlefish
复制代码
# 一次性启动3个实例
launch_cvd --num_instances=3

# 每个实例分配独立资源:
# - ADB端口
# - WebRTC端口
# - Console端口

60.9.8 进阶:自定义内核

复制代码
# 步骤1:拉取内核源码
repo init -u https://android.googlesource.com/kernel/manifest \
    -b common‑android‑mainline
repo sync

# 步骤2:编译内核
BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh

# 步骤3:模拟器加载自定义内核启动
emulator -kernel /path/to/bzImage \
    -system $ANDROID_PRODUCT_OUT/system.img \
    -vendor $ANDROID_PRODUCT_OUT/vendor.img

60.9.9 理解产品配置继承链

编译模拟器镜像时产品配置存在明确继承链。以 x86_64 phone 目标举例:

cpp 复制代码
sdk_phone64_x86_64.mk
  -> phone.mk
       -> handheld.mk
            -> base_handheld.mk
                 -> multidisplay.mk
            -> generic_system.mk
            -> handheld_system_ext.mk
            -> aosp_product.mk
       -> base_phone.mk
            -> generic.mk
                 -> versions.mk
            -> phone_overlays.mk

各层职责:

  • versions.mk ------ 设定版本号与 API 等级
  • generic.mk ------ 模拟器设备核心配置:HAL、软件包、系统属性
  • base_handheld.mk ------ 手持设备专属配置(Dalvik 堆内存等)
  • handheld.mk ------ 完整手持设备产品配置,包含 system、system_ext、product 分区
  • base_phone.mk ------ 手机专属资源覆盖包
  • phone.mk ------ 整合全部配置,补充 config.ini 文件

分层设计:新增模拟器产品形态,只需要编写薄薄一层顶层 mk,继承对应基础配置即可。

60.9.10 HAL 实现测试

平台开发者非常适合在模拟器调试 HAL 实现。以修改传感器 HAL 流程为例:

复制代码
# 步骤1:修改传感器HAL源码
# device/generic/goldfish/hals/sensors/multihal_sensors.cpp

# 步骤2:单独编译传感器模块
m android.hardware.sensors@2.1‑impl.ranchu

# 步骤3:推送更新库文件
adb root
adb remount
adb push $ANDROID_PRODUCT_OUT/vendor/lib64/hw/\
    android.hardware.sensors@2.1‑impl.ranchu.so \
    /vendor/lib64/hw/

# 步骤4:重启HAL服务
adb shell stop
adb shell start

# 步骤5:验证
adb shell dumpsys sensorservice

Camera HAL 示例:

复制代码
# 重新编译camera provider
m android.hardware.camera.provider.ranchu

# 推送重启
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

# 验证摄像头
adb shell dumpsys media.camera

60.9.11 追踪模拟器通信

调试客户机 HAL 与 QEMU 宿主机交互,可用下面几种手段:

  1. logcat 日志过滤

    传感器HAL日志

    adb logcat -s goldfish:V MultihalSensors:V

    Camera HAL日志

    adb logcat -s CameraProvider:V QemuCamera:V

    GNSS HAL日志

    adb logcat -s GnssHwConn:V GnssHwListener:V

    Radio HAL日志

    adb logcat -s RadioModem:V AtChannel:V

  2. 对 HAL 服务使用 strace 跟踪系统调用

    查找HAL进程PID

    adb shell ps -A | grep sensors

    挂载strace跟踪read write ioctl

    adb shell strace -p -e trace=read,write,ioctl -s 256

  3. QEMU 监控命令

通过模拟器控制台(telnet 连接控制台端口)查看虚拟设备状态:

复制代码
info qtree      # 设备树
info mtree      # 内存布局
info ioports    # IO端口

60.9.12 编译精简模拟器镜像

CI/CD 场景对启动时间、镜像大小敏感,可以构建 slim 变体,裁剪非必要组件。

复制代码
lunch sdk_slim_x86_64‑userdebug
m -j$(nproc)

slim 变体来自 device/generic/goldfish/product/slim_handheld.mk,直接继承 generic.mk,不引入完整手持产品继承链,镜像更小、启动更快。

60.9.13 在模拟器上运行 CTS

模拟器是官方支持的 CTS(兼容性测试套件)运行目标。

复制代码
# 步骤1:启动满足CTS要求的模拟器
emulator -gpu host -memory 4096 -cores 4

# 步骤2:等待开机完成
adb wait‑for‑device
adb shell getprop sys.boot_completed  # 返回1代表开机完成

# 步骤3:执行CTS
cd /path/to/cts
./android‑cts/tools/cts‑tradefed
> run cts --plan CTS

# 也可以运行指定测试模块
> run cts -m CtsMediaTestCases
> run cts -m CtsSensorTestCases

部分 CTS 用例依赖特定传感器数值与硬件能力;模拟器传感器 HAL 全部开启 DATA_INJECTION 标记,专门用于满足 CTS 兼容性测试。

60.9.14 模拟器控制台命令

模拟器提供 telnet 控制台直接下发控制指令。

复制代码
# 连接控制台
telnet localhost 5554

# 电源模拟
power capacity 50          # 电量设置50%
power status charging      # 设置充电状态

# 网络模拟
network speed gsm          # GSM速率模拟
network delay gprs         # GPRS网络时延模拟

# 短信模拟
sms send 5551234567 Hello from the console!

# GPS定位
geo fix -122.084 37.422   # 设置GPS到谷歌总部坐标

# 传感器模拟
sensor set acceleration 0:9.8:0  # 设置加速度计

# 指纹模拟
finger touch 1             # 模拟指纹按压

# 快照管理
avd snapshot save mysnap
avd snapshot load mysnap
avd snapshot list

小结

Android 模拟器是一套复杂的虚拟化平台,可在虚拟硬件上运行真实的 Android 系统镜像。本章梳理了它的分层架构:

  1. QEMU 核心 ------ 依托 KVM(硬件虚拟化)或 TCG(软件指令翻译)实现 CPU 虚拟化,同时提供内存管理与设备模拟能力。
  2. Goldfish/Ranchu 虚拟平台 ------ 由一系列虚拟设备构成,包含 VirtIO 设备(显卡、网卡、块设备、输入设备、控制台、随机数发生器),以及用于宿主机‑虚拟机间高带宽通信的 goldfish‑pipe 通道。
  3. HAL 实现层 ------ 共九个 HAL 模块(音频、相机、传感器、GNSS、射频、指纹、HWC3、图形内存分配器,以及配套支撑库),作为 Android 硬件接口与模拟器虚拟设备之间的适配桥接层。
  4. GPU 模拟 ------ 采用命令流架构:虚拟机侧的 GLES/Vulkan 调用被序列化,经由 goldfish‑pipe 发送至宿主机,最终在宿主机真实 GPU 上执行回放。
  5. 网络子系统 ------ 包含虚拟路由、VirtIO 无线网卡、端口转发、多模拟器互通能力。
  6. Cuttlefish ------ 面向云端的替代方案,复用同一内核与 VirtIO 设备,无需桌面图形界面运行,适合 CI/CD 与服务器自动化测试场景。
  7. 开发者功能 ------ 支持快照快速回滚、多显示器、折叠屏仿真、定位 / 电池 / 电话模拟,以及丰富的控制台调试指令。
  8. Android 17 版本 Cuttlefish 变更点 ------ 新增aosp_cf_x86_64_desktop产品配置;虚拟机端提供vkms_controller显示控制工具;通过process_sandboxer对宿主机守护进程扩充独立可执行文件级别的 seccomp 沙箱;新增 Rust 实现的 NPU 调度 HAL;提供 20 端口 HVC 映射(传感器控制通道与数据通道分离);cvd启动器与 Debian 软件包迁移至外部仓库github.com/google/android‑cuttlefish。

整套架构的核心设计原则:模拟器运行原生真实 Android 系统------ 内核、框架、系统镜像格式与实体设备完全一致。虚拟硬件层对上层软件做到透明无感,保证应用与平台代码在真机和模拟器上行为完全相同。

核心架构原则:模拟器运行真实 Android------ 内核、框架、系统镜像格式与真机完全一致。虚拟硬件层对上层软件透明,应用、平台代码行为在模拟器和物理设备保持一致。

关键源码参考

文件 用途
device/generic/goldfish/AndroidProducts.mk 产品目标定义
device/generic/goldfish/board/BoardConfigCommon.mk 板级通用配置
device/generic/goldfish/product/generic.mk 模拟器核心产品配置
device/generic/goldfish/product/handheld.mk 手持设备产品配置
device/generic/goldfish/init/init.ranchu.rc 初始化脚本,开机流程
device/generic/goldfish/hals/sensors/multihal_sensors.cpp 传感器 HAL 主体逻辑
device/generic/goldfish/hals/sensors/multihal_sensors_qemu.cpp QEMU 传感器协议
device/generic/goldfish/hals/sensors/sensor_list.cpp 传感器清单定义
device/generic/goldfish/hals/camera/CameraProvider.cpp Camera Provider
device/generic/goldfish/hals/camera/qemu_channel.cpp Camera QEMU pipe 通信
device/generic/goldfish/hals/gnss/GnssHwConn.cpp GNSS 硬件连接
device/generic/goldfish/hals/radio/RadioModem.cpp Radio Modem HAL
device/generic/goldfish/hals/fingerprint/hal.cpp 指纹 HAL
device/generic/goldfish/hals/hwc3/HostFrameComposer.cpp 宿主机端 GPU 合成
device/generic/goldfish/hals/hwc3/GuestFrameComposer.cpp 客户机 DRM 合成
device/generic/goldfish/hals/gralloc/allocator.cpp 图形缓冲区分配器
device/generic/goldfish/hals/lib/qemud/qemud.cpp QEMU 多路复用 pipe 库
device/generic/goldfish/qemu‑props/qemu‑props.cpp qemu‑props 属性服务
device/generic/goldfish/init/init.net.ranchu.sh 网络初始化脚本
device/generic/goldfish/sepolicy/vendor/qemu_props.te qemu‑props SELinux 策略
device/generic/goldfish/sepolicy/vendor/hal_gnss_default.te GNSS HAL SELinux 策略
device/google/cuttlefish/shared/BoardConfig.mk Cuttlefish 板级配置、内核选择
device/google/cuttlefish/AndroidProducts.mk Cuttlefish 产品清单,包含桌面
device/google/cuttlefish/vsoc_x86_64_only/desktop/aosp_cf.mk Android17 新增桌面产品
device/google/cuttlefish/host/libs/vm_manager/crosvm_manager.cpp crosvm 命令组装、HVC 端口映射
device/google/cuttlefish/host/commands/process_sandboxer/main.cpp Android17 新增宿主机进程沙箱
device/google/cuttlefish/guest/commands/vkms_controller/main.cpp Android17 客户机 VKMS 显示控制器
device/google/cuttlefish/guest/hals/npu/main.rs Android17 Rust 实现 NPU 调度 HAL
device/google/cuttlefish/shared/device.mk Cuttlefish 公共设备配置,安装 vkms_controller
device/google/cuttlefish/README.md Cuttlefish 快速上手,宿主机工具迁移说明
相关推荐
水巷石子2 小时前
备考系统架构设计师第一天
学习·架构·软考·设计·考试
武子康2 小时前
商业比较词进入 AI Overview:Semrush 60 万关键词研究能说明什么
人工智能·ai·架构·agent·claude·codex·semrush
m0_587383003 小时前
全民健身解决方案软件开发实战:从架构设计到落地指南
java·spring boot·spring·架构·需求分析
沪上企服通4 小时前
数电发票全生命周期下的企业财税中台:从“开票工具“到“实时风控“的架构跃迁
架构
lytao1236 小时前
单体、微服务、Serverless:架构要匹配问题
微服务·架构·serverless·软件工程
javaDocker7 小时前
金融级 AIOps 架构跃迁(从“1-5-10“快恢目标到 AI Agent 告警收敛的完整技术实践)
人工智能·金融·架构
数字孪生视频孪生8 小时前
三维实时重构异构底座 核工危化无感定位跨境轨迹一屏统揽
大数据·运维·人工智能·重构·架构
数脉9 小时前
让每一条数据都有来龙去脉:我们的列级数据血缘平台功能全景
架构
Forerror20269 小时前
深入理解大模型网关是什么:MAI Gateway架构与核心价值解读
架构·gateway