自 2025 年 3 月 27 日起,我们建议您使用 android-latest-release
而非 aosp-main
构建 AOSP 并为其做出贡献。如需了解详情,请参阅 AOSP 的变更。
Microdroid
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
Microdroid 是在 pVM 中运行的迷你版 Android OS。您可以启动运行任何操作系统的虚拟机,并非必须使用 Microdroid。但是,pVM 的主要用例不是运行独立的操作系统,而是提供一个隔离的执行环境,以比常规 Android 环境更高的机密性和完整性运行应用的某一部分。
在传统操作系统中,要想提供强大的机密性和完整性保护,就必须进行大量的工作(而且通常是重复性的工作)。这是因为传统操作系统无法与整体 Android 架构保持良好兼容。例如,就标准 Android 架构而言,开发者需要实现一种方法,以便在 pVM 中安全地加载和执行应用的某个部分,同时也需要依据 glibc 来构建载荷。但是,Android 应用需要使用 Bionic,通信需要使用基于 vsock 的自定义协议,而调试则需要使用 adb 且颇具难度。
Microdroid 可以轻松弥合这些差异。它提供了一个精心设计的现成操作系统映像,让开发者能以最省力的方式把应用的某个部分分流到 pVM 中。Microdroid 的原生代码基于 Bionic,通信则是通过 Binder 来实现。它支持从主机 Android 导入 APEX,并且公开了一部分 Android API,例如在使用由硬件支持的密钥进行加密操作时需要用到的密钥库。总而言之,Microdroid 为开发者提供了一个熟悉的环境,其中包含他们早已熟知的完整版 Android OS 中的各种工具。
功能
Microdroid 是 Android 的精简版本,并且添加了一些专门面向 pVM 的组件。Microdroid 支持以下功能:
- 一部分 NDK API(Android 用于实现 libc 和 Bionic 的所有 API 均包括在内)
- 调试功能,例如 adb、logcat、tombstone 和 gdb
- 启动时验证和 SELinux
- 加载和执行二进制文件以及 APK 中嵌入的共享库
- 通过 vsock 支持 Binder RPC;在交换文件时执行隐式完整性检查
- 加载 APEX
Microdroid 不支持以下功能:
Microdroid 架构
Microdroid 与 Cuttlefish 十分相似,二者都具有类似标准 Android 的架构。Microdroid 包含以下分区映像,这些映像在一个复合磁盘映像中进行了分组:
bootloader
- 验证并启动内核。
boot.img
- 包含内核和 init ramdisk。
vendor_boot.img
- 包含虚拟机专用的内核模块,例如 virtio。
super.img
- 包含系统和供应商逻辑分区。
vbmeta.img
- 包含经过验证的启动元数据。
这些分区映像整合在一个虚拟化 APEX 中,并通过 VirtualizationService
打包成一个复合磁盘映像。VirtualizationService
不仅用于生成这个主操作系统复合磁盘映像,还负责创建下面列出的其他分区:
payload
- 一组由 Android 的 APEX 和 APK 支持的分区
instance
- 用于按实例持久保留启动时验证数据的加密分区,这类数据包括每个示例的 salt 值、可信的 APEX 公钥、回滚计数器等
启动序列
Microdroid 启动序列在设备启动后触发。关于设备启动的具体内容,在架构文档的“pVM 固件”部分中有详细的讨论。图 1 显示了 Microdroid 启动序列过程中的具体步骤:
图 1. Microdroid 实例的安全启动流程
以下是对这些步骤的说明:
crosvm 将引导加载程序加载到内存,pvmfw 开始执行。在跳转到引导加载程序之前,pvmfw 会执行两项任务:
- 验证引导加载程序,检查其是否来自可信来源(Google 或 OEM)。
- 确保同一 pVM 在使用同一实例映像进行多次启动的过程中,会始终如一地使用相同的引导加载程序。具体而言,pVM 最初会使用空白的实例映像进行启动。pvmfw 会存储该实例映像中的引导加载程序的身份信息,并对其进行加密。当 pVM 下次使用同一实例映像启动时,pvmfw 会解密已保存的该实例映像的身份信息,并验证当前的身份信息是否与先前保存的信息完全相同。如果身份信息不同,pvmfw 将拒绝启动。
引导加载程序启动 Microdroid。
引导加载程序访问实例磁盘。与 pvmfw 类似,引导加载程序会在一个实例磁盘中存储同一实例上次启动时使用的分区映像的相关信息,包括公钥信息。
引导加载程序验证 vbmeta 和链式分区(例如 boot
和 super
)。如果验证成功,则会推导出下一阶段的 pVM Secret。然后,Microdroid 会将控制权移交给内核。
由于引导加载程序已经在第 3 步验证了 super 分区,所以内核会无条件地安装 super 分区。与完整版 Android 一样,super 分区包含了多个通过 dm-verity 安装的逻辑分区。随后,控制权将移交给 init
进程,该进程会启动各种原生服务。init.rc
脚本也与完整版 Android 中的十分相似,只不过针对 Microdroid 的需求进行了一些调整。
init
进程启动 Microdroid 管理器,该管理器会访问实例映像。Microdroid 管理器服务使用前一阶段传递的密钥解密映像,并读取该 pVM 信任的客户端 APK 和 APEX 的公钥及回滚计数器信息。这些信息稍后会在安装客户端 APK 和请求的 APEX 时,分别用于 zipfuse
和 apexd
。
Microdroid 管理器服务启动 apexd
。
apexd
在 /apex/<name>
目录下安装 APEX。在 APEX 的安装方式上,Android 与 Microdroid 只有一个差别:在 Microdroid 中,APEX 文件来自虚拟块设备(例如 /dev/vdc1
),而非来自常规文件 (/system/apex/*.apex
)。
zipfuse
是 Microdroid 的 FUSE 文件系统。zipfuse
会将客户端 APK(基本上是一个 Zip 文件)作为文件系统进行安装。在底层,pVM 会将 APK 文件作为一个虚拟块设备通过 dm-verity 进行传递,就像 APEX 一样。APK 中包含一个配置文件,其中包括了应用开发者针对该 pVM 实例请求的 APEX 的列表。在激活 APEX 时,该列表会被 apexd
使用。
启动流程返回到 Microdroid 管理器服务。然后,管理器服务会使用 Binder RPC 与 Android 的 VirtualizationService
进行通信,以便能够报告崩溃或关闭等重要事件,并接受诸如终止 pVM 之类的请求。管理器服务还会从 APK 的配置文件中读取主二进制文件的位置,并加以执行。
文件交换 (AuthFS)
Android 组件使用文件进行输入、输出和状态记录,并将这些文件作为由 Android 内核控制访问的文件描述符(AIDL 中的 ParcelFileDescriptor
类型)进行传递,是十分常见的。AuthFS 就提供了这样的功能,以便能够跨 pVM 边界在互不信任的端点之间交换文件。
基本而言,AuthFS 是一个远程文件系统,它会对每次访问操作进行透明的完整性检查,类似于 fs-verity
。这些检查使前端(例如 pVM 中运行的文件读取程序)能够检测不受信任的后端(通常为 Android)是否包含经过篡改的文件内容。
要进行文件交换,后端 (fd\_server
) 会在通信开始时提供每个文件的配置,以表明它是输入(只读)还是输出(读写)。如果是输入,前端会强制要求相关内容与 Merkle 树顶层的已知哈希完全匹配,以实现访问时验证。如果是输出,AuthFS 会在内部将相关内容的哈希树保存为写入操作的观察结果,并在回读数据时强制执行完整性检查。
目前,底层传输是基于 Binder RPC 进行的。但是为了优化性能,此机制今后可能会有所变化。
密钥管理
pVM 具有一个稳定的密封密钥和一个认证密钥,前者非常适合用于受保护的持久性数据,后者非常适合用于生成由 pVM 验证生成的签名。
Binder RPC
大多数 Android 接口都用 AIDL 表示,而 AIDL 是基于 Binder Linux 内核驱动程序构建的。为了在 pVM 之间提供接口支持,Binder 协议经过重写,以便能够通过套接字使用(对于 pVM,该套接字为 vsock)。通过这种方式,Android 现有的 AIDL 接口就能在 pVM 这个新环境中使用。
要设置连接,终端(例如 pVM 载荷)需要创建一个 RpcServer
对象、注册一个 root 对象,并进入监听新连接的状态。要连接到该服务器,客户端需要使用 RpcSession
对象并获取 Binder
对象,然后像在 Binder 内核驱动程序中使用 Binder
对象一样使用该对象。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-03-04。
[null,null,["最后更新时间 (UTC):2025-03-04。"],[],[],null,["# Microdroid is a mini-Android OS that runs in a pVM. You don't have to use\nMicrodroid, you can start a VM with any OS. However, the primary use cases\nfor pVMs aren't running a standalone OS but rather offering an isolated\nexecution environment for running a portion of an app with stronger\nconfidentiality and integrity guarantees than Android can provide.\n\nWith traditional operating systems, providing strong confidentiality and\nintegrity requires a fair amount of work (often duplicated)\nbecause traditional operating systems don't fit with the overarching Android\narchitecture. For example, with the standard Android architecture, developers\nneed to implement a means of securely loading and executing part of their app\nin the pVM, and the payload is built against glibc. The Android app uses\nBionic, communication requires a custom protocol over vsock, and debugging using\nadb is challenging.\n\nMicrodroid fills these gaps by providing an off-the-shelf OS image designed to\nrequire the least amount of effort from developers to offload a portion of\ntheir app into a pVM. Native code is built against Bionic, communication happens\nover Binder, and it allows importing APEXes from the host Android and exposes\na subset of the Android API, such as keystore for cryptographic operations with\nhardware-backed keys. Overall, developers should find Microdroid a familiar\nenvironment with the tools they've grown accustomed in the full Android OS.\n\nFeatures\n--------\n\nMicrodroid is a stripped down version of Android with a few additional\ncomponents specific to pVMs. Microdroid supports:\n\n- A subset of NDK APIs (all APIs for Android's implementation of libc and Bionic are provided)\n- Debugging features, such as adb, logcat, tombstone, and gdb\n- Verified Boot and SELinux\n- Loading and executing a binary, together with shared libraries, embedded in an APK\n- Binder RPC over vsock and exchange of files with implicit integrity checks\n- Loading of APEXes\n\nMicrodroid doesn't support:\n\n- Android Java APIs in the `android.\\*` packages\n\n | **Note:** Core Java APIs in the `java.\\*` packages can be supported by activating the ART APEX in the VM.\n- SystemServer and Zygote\n\n- Graphics/UI\n\n- HALs\n\nMicrodroid architecture\n-----------------------\n\nMicrodroid is similar to\n[Cuttlefish](/docs/devices/cuttlefish) in that both have an architecture that's\nsimilar to standard Android. Microdroid consists of the following partition\nimages grouped together in a composite disk image:\n\n- `bootloader` - Verifies and starts the kernel.\n- `boot.img` - Contains the kernel and init ramdisk.\n- `vendor_boot.img` - Contains VM-specific kernel modules, such as virtio.\n- `super.img` - Consists of system and vendor logical partitions.\n- `vbmeta.img` - Contains verified boot metadata.\n\nThe partition images ship in the Virtualization APEX and are packaged in\na composite disk image by `VirtualizationService`. In addition to the main\nOS composite disk image, `VirtualizationService` is responsible for creating\nthese other partitions:\n\n- `payload` - A set of partitions backed by Android's APEXes and APKs\n- `instance` - An encrypted partition for persisting per-instance verified boot data, such as per-instance salt, trusted APEX public keys, and rollback counters\n\nBoot sequence\n-------------\n\nThe Microdroid boot sequence occurs after\n[Device boot](/docs/core/virtualization/architecture#device-boot). Device boot is\ndiscussed in the pVM Firmware section of the [Architecture](/docs/core/virtualization/architecture)\ndocument. Figure 1 shows the steps that take place during the Microdroid boot\nsequence:\n\n\n**Figure 1.** Secure bootflow of microdroid instance\n\n\u003cbr /\u003e\n\nHere's an explanation of the steps:\n\n1. The bootloader is loaded into memory by crosvm and pvmfw starts\n executing. Before jumping to the bootloader, pvmfw performs two tasks:\n\n - Verifies the bootloader to check if it is from a trusted source (Google or an OEM).\n - Ensures that the same bootloader is used consistently across multiple boots of the same pVM through the use of the instance image. Specifically, the pVM is initially booted with an empty instance image. pvmfw stores the identity of the bootloader in the instance image and encrypts it. So, the next time the pVM is booted with the same instance image, pvmfw decrypts the saved identity from the instance image and verifies that it's the same that was previously saved. If the identities differ, pvmfw refuses to boot.\n\n The bootloader then boots Microdroid.\n2. The bootloader accesses the instance disk. Similar to pvmfw, the\n bootloader has an instance disk drive with information about partition images\n used in this instance during previous boots, including the public key.\n\n3. The bootloader verifies vbmeta and the chained partitions, such as `boot`\n and `super`, and, if successful, derives the next-stage pVM secrets.\n Then, Microdroid hands control over to the kernel.\n\n4. Because the super partition has already been verified by the bootloader\n (step 3), the kernel unconditionally mounts the super partition. As with the\n full Android, the super partition consists of multiple logical partitions\n mounted over dm-verity. Control is then passed to the `init` process, which\n starts various native services. The `init.rc` script is similar to that of full\n Android but tailored to the needs of Microdroid.\n\n5. The `init` process starts the Microdroid manager, which accesses the instance\n image. The Microdroid manager service decrypts the image using the key passed\n from the previous stage and reads the public keys and rollback counters of the\n client APK and APEXes that this pVM trusts. This information is used later by\n `zipfuse` and `apexd` when they mount the client APK and requested APEXes,\n respectively.\n\n6. The Microdroid manager service starts `apexd`.\n\n7. `apexd` mounts the APEXes at `/apex/\u003cname\u003e` directories. The only difference\n between how Android and Microdroid mount APEXes is that in\n Microdroid, the APEX files are coming from virtual block devices\n (`/dev/vdc1`, ...), not from regular files (`/system/apex/*.apex`).\n\n8. `zipfuse` is Microdroid's FUSE file system. `zipfuse` mounts the client APK,\n which is essentially a Zip file as a file system. Underneath, the APK file is\n passed as a virtual block device by the pVM with dm-verity, same as APEX. The\n APK contains a config file with a list of APEXes that the app developer has\n requested for this pVM instance. The list is used by `apexd` when activating\n APEXes.\n\n9. The boot flow returns to the Microdroid manager service. The manager\n service then communicates with Android's `VirtualizationService` using\n Binder RPC so that it can report important events like crash or shutdown,\n and accept requests such as terminating the pVM. The manager service reads the\n location of the main binary from the APK's config file and executes it.\n\nFile exchange (AuthFS)\n----------------------\n\nIt's common for Android components to use files for input, output, and state\nand to pass these around as file descriptors (`ParcelFileDescriptor` type in\nAIDL) with access controlled by the Android kernel. AuthFS facilitates similar\nfunctionality for exchanging files between mutually distrusting endpoints\nacross pVM boundaries.\n\nFundamentally, AuthFS is a remote file system with transparent integrity checks\non individual access operations, similar to `fs-verity`. The checks allow the\nfrontend, such as a file-reading program running in a pVM, to detect if the\nuntrusted backend, typically Android, tampered with file content.\n\nTo exchange files, the backend (`fd\\_server`) is started with per-file\nconfiguration specifying whether it's meant for input (read-only) or output\n(read-write). For input, the frontend enforces that the contents match a known\nhash, on top of a Merkle tree for on-access verification. For output, AuthFS\ninternally maintains a hash tree of the contents as observed from write\noperations and can enforce integrity when the data are read back.\n\nThe underlying transport is currently based on Binder RPC, however that might\nchange in the future to optimize performance.\n\nKey management\n--------------\n\npVMs are provided with a stable *sealing key* that's suitable for protecting\npersistent data, and an *attestation key* that's suitable for producing\nsignatures that are verifiably produced by the pVM.\n\nBinder RPC\n----------\n\nA majority of Android's interfaces are expressed in\n[AIDL](https://developer.android.com/guide/components/aidl), which\nis built on top of the Binder Linux kernel driver. To support interfaces\nbetween pVMs, the Binder protocol has been rewritten to work over sockets,\n[vsock](/docs/core/virtualization/virtualization-service#vm-sockets)\nin the case of pVMs. Operating over sockets allows Android's existing AIDL\ninterfaces to be used in this new environment.\n\nTo set up the connection, one endpoint, such as pVM payload, creates\nan `RpcServer` object, registers a root object, and begins listening for new\nconnections. Clients can connect to this server using an `RpcSession` object,\nget the `Binder` object, and use it exactly like a `Binder` object is used\nwith the kernel Binder driver."]]