comprehensive-rust 教程:在 AOSP 中构建、部署与调用 Rust Binder 服务(birthday_server 实战指南)
2026/9/10 15:41:04 网站建设 项目流程

comprehensive-rust 教程:在 AOSP 中构建、部署与调用 Rust Binder 服务(birthday_server 实战指南)

【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust

本指南以 comprehensive-rust 课程中 Android AIDL 章节的deploy一课为核心,完整讲解如何把用 Rust 编写的 Binder 服务birthday_server构建、推送到设备并启动,再通过service checkservice call验证其可用性。读完本文,你将掌握 AOSP Soong 构建、adb部署、服务注册检查与 Parcel 结果解析的完整闭环,能够独立将课程中的 Birthday Service 示例跑通。

背景:我们要部署什么

在课程中,Birthday Service 是一套完整的"接口 — 服务实现 — 服务端 — 客户端"示例,用于演示用 Rust 调用 Android Binder。整个流程由 src/android/aidl/birthday-service.md 起头,依次经过:

  1. 定义 AIDL 接口IBirthdayService(见 src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl);
  2. 在 Rust 中实现该接口(src/android/aidl/birthday_service/src/lib.rs);
  3. 编写服务端进程birthday_server(src/android/aidl/birthday_service/src/server.rs)与客户端birthday_client(src/android/aidl/birthday_service/src/client.rs)。

deploy这一步要解决的是:当代码写完之后,如何在真实 AOSP 环境里把它变成设备上可用的系统服务deploy.md原文(src/android/aidl/example-service/deploy.md)给出的核心操作只有"build、push、start"三个词,但其背后依赖的构建脚本与 Binder 注册机制,正是本文要展开的部分。

部署前置条件

在运行部署命令之前,需要满足以下环境要求(依据课程构建脚本 src/android/build_all.sh 的头部注释整理):

  • AOSP 源码树:脚本必须在 AOSP checkout 中执行,且需要完成source build/envsetup.shlunch(课程示例使用aosp_cf_x86_64_phone-userdebug目标,即 Cuttlefish 虚拟设备镜像);
  • 可用的adb环境:已连接设备或模拟器,adb正常工作;
  • m能看到课程源码:AOSP 的构建系统只能看到放在源码树内的Android.bp,因此需要把课程仓库放进 AOSP checkout,或用 bind mount 挂载,例如:
cd "$ANDROID_BUILD_TOP" mkdir comprehensive-rust sudo mount -o bind ../path/to/comprehensive-rust/src comprehensive-rust

挂载后即可用m hello_rust之类的目标验证构建系统是否识别课程中的模块。build_all.sh中所有m目标(如birthday_serverbirthday_clienthello_rust等)正是依赖这种方式才能被 AOSP 发现并编译。

第一步:构建并启动服务

deploy.md中引用的构建、推送、启动命令,实际来自 src/android/build_all.sh 中birthday_server函数内的ANCHOR: birthday_server代码段。原文档通过 mdBook 的{{#include}}机制将脚本片段嵌入页面,展开后的完整命令如下:

m birthday_server adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_server" /data/local/tmp adb root adb shell /data/local/tmp/birthday_server

逐条解释:

命令作用
m birthday_server调用 Soong 构建系统编译birthday_server这个rust_binary目标,产物输出到$ANDROID_PRODUCT_OUT/system/bin/
adb push ... /data/local/tmp把编译出的二进制推送到设备上的/data/local/tmp目录
adb root以 root 身份重启 adbd。/data/local/tmp需要 root 权限运行,且注册 Binder 系统服务通常也需要较高权限
adb shell /data/local/tmp/birthday_server在设备上直接启动服务进程,它会在前台运行并开始监听 Binder 请求

需要特别注意的是:birthday_server是一个前台进程,启动后会一直占用终端(因为服务端调用了join_thread_pool阻塞等待请求)。因此在build_all.sh中,脚本用birthday_server &将其放到后台运行并记录 PID,部署一节也要求"在另一个终端中检查服务状态"。这与客户端birthday_client不同——后者是短生命周期程序,调用完即退出。

构建目标对应的 Soong 配置

m birthday_server之所以能够成立,是因为 src/android/aidl/birthday_service/Android.bp 中定义了对应的 Soong 模块:

rust_binary { name: "birthday_server", crate_name: "birthday_server", srcs: ["src/server.rs"], rustlibs: [ "com.example.birthdayservice-rust", "libbirthdayservice", ], prefer_rlib: true, // To avoid dynamic link error. }

关键点:

  • rustlibs引用了两个依赖:com.example.birthdayservice-rust(AIDL 接口生成的 Rust crate)与libbirthdayservice(服务实现所在的rust_library,定义见同文件ANCHOR: libbirthdayservice段);
  • prefer_rlib: true注释明确说明是为了避免动态链接错误,即要求静态链接 Rust 库,保证二进制在设备上可独立运行;
  • AIDL 接口 crate 本身由 src/android/aidl/birthday_service/aidl/Android.bp 中的aidl_interface模块生成,其中backend.rust.enabled = true表明Rust 后端默认未启用,需要显式打开,这是 Rust AIDL 与 Java/C++ 后端的显著差异。

第二步:用service check验证服务已注册

服务启动后,在另一个终端执行service check确认它已经被 Android 服务管理器(ServiceManager)登记:

adb shell service check birthdayservice

预期输出:

Service birthdayservice: found

这里birthdayservice正是服务端代码 src/android/aidl/birthday_service/src/server.rs 中定义的注册名:

const SERVICE_IDENTIFIER: &str = "birthdayservice"; ... binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder()) .expect("Failed to register service");

service check返回found意味着注册成功,也印证了部署流程中"启动服务 → 注册到 ServiceManager"这一环是通的。build_all.sh中还有一个配套细节:脚本在启动birthday_server后会用循环等待服务真正注册完成:

while adb shell service check birthdayservice | grep -q 'not found'; do echo "Waiting on birthdayservice..." sleep 3 done

这说明服务注册是异步过程——进程启动后需要一点时间完成 Binder 初始化与add_service调用,立即service check可能得到not found

服务端注册的完整机制

server.rsmain函数完整展示了 Rust Binder 服务的注册四步曲(deploy 的"start"环节能成立的原因):

fn main() { let birthday_service = BirthdayService; // 1. 创建服务实例 let birthday_service_binder = BnBirthdayService::new_binder( // 2. 包装为 Bn* 类型 birthday_service, binder::BinderFeatures::default(), ); binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder()) // 3. 注册 .expect("Failed to register service"); binder::ProcessState::join_thread_pool(); // 4. 加入线程池 }
  • BirthdayService是 src/android/aidl/birthday_service/src/lib.rs 中实现IBirthdayServicetrait 的类型;
  • BnBirthdayService由 AIDL 编译器生成,相当于 C++ Binder 世界中的BnBinder基类角色;由于 Rust 没有继承,这里通过组合把自定义服务实例放进生成的BnBirthdayService中;
  • binder::add_service将服务以字符串标识注册进 ServiceManager;
  • join_thread_pool让当前线程加入 Binder 线程池开始监听连接——这正是服务端进程不退出、持续响应请求的原因。

课程中还特别强调:wishHappyBirthday等 AIDL 方法签名是&self而非&mut self,因为 Binder 在线程池中并行处理多个请求,服务方法只能拿到共享引用;若服务需要可变状态,应放入Mutex之类的同步原语中。

第三步:用service call直接调用接口方法

deploy.md进一步演示了不写客户端代码、直接用service call从 shell 调用 Binder 方法的技巧:

adb shell service call birthdayservice 1 s16 Bob i32 24

该命令的参数含义为:

参数含义
birthdayservice服务注册名(与service check一致)
1调用的接口方法索引:AIDL 接口中方法从 1 开始编号,1对应IBirthdayService中第一个方法wishHappyBirthday(见 IBirthdayService.aidl 中方法声明顺序)
s16 Bob第一个参数,UTF-16 字符串"Bob",对应String name
i32 24第二个参数,32 位整数24,对应int years

返回结果是原始 Parcel 十六进制转储:

Result: Parcel( 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.' 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.' 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .' 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.' 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .' 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.' 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.' 0x00000070: 00210073 00000000 's.!..... ')

这段转储的可读文本列(右侧 ASCII 区)把每个 16 位字解码后拼出了:

Happy Birthday Bob, congratulations with the 24 years!

这正是lib.rswishHappyBirthday的实现逻辑:

fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String> { Ok(format!("Happy Birthday {name}, congratulations with the {years} years!")) }

同时也可以从转储中看到 Binder 字符串的编码特点:文本以 UTF-16 存储(注意00610048等每个字符占 2 字节),00000036是长度为 0x36=54 个字符的数据长度前缀。理解这条命令与输出,就能在没有客户端的情况下快速验证接口的行为,是排查服务端逻辑问题的利器。

方法索引的确定方式

方法索引1不是随意取的。对照 IBirthdayService.aidl:

interface IBirthdayService { String wishHappyBirthday(String name, int years); // transaction code 1 String wishWithInfo(in BirthdayInfo info); // 2 String wishWithProvider(IBirthdayInfoProvider provider); // 3 String wishWithErasedProvider(IBinder provider); // 4 String wishFromFile(in ParcelFileDescriptor infoFile); // 5 }

wishHappyBirthday位于首位,故交易码为1。若要调用其他方法,需要相应调整索引,并注意其参数类型的编码(例如wishWithInfo需要传入 parcelable,wishFromFile需要传文件描述符)。

部署后的完整验证链路

deploy.md之外,build_all.sh还给出了与部署配套的客户端验证方式(属于ANCHOR: birthday_client段),可作为部署成功的最终确认:

m birthday_client adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_client" /data/local/tmp adb shell /data/local/tmp/birthday_client Charlie 60

客户端源码(src/android/aidl/birthday_service/src/client.rs)通过binder::get_interface::<dyn IBirthdayService>(SERVICE_IDENTIFIER)获取服务端接口,然后调用wishHappyBirthday(&name, years)打印结果。与service call相比,客户端走的是类型安全的 Rust API,而非手写交易码。

部署与验证的完整时序可归纳为:

  1. m birthday_server构建;
  2. adb push+adb shell .../birthday_server启动服务(前台/后台皆可);
  3. adb shell service check birthdayservice轮询直到输出found
  4. 选择验证方式:
    • 快速验证:adb shell service call birthdayservice 1 s16 Bob i32 24
    • 类型安全验证:编译并运行birthday_client
  5. 验证结束,杀掉服务进程(build_all.sh中为pkill -f birthday_server)。

常见问题与排查要点

结合构建脚本与源码,部署环节最容易踩的坑集中在以下几点:

  • m birthday_server找不到目标:课程仓库未挂载进 AOSP checkout。按前置条件一节执行 bind mount,并确认m hello_rust能正常工作;
  • service check长时间not found:服务可能未启动成功或注册名拼写不一致。核对SERVICE_IDENTIFIER常量(birthdayservice)与命令中的名称是否一致;也可检查服务进程是否因 panic 崩溃(add_service失败会触发expect直接退出);
  • service call返回异常 Parcel:交易码或参数类型写错。核对方法索引与 AIDL 声明顺序,确认参数类型标记(s16i32)与方法签名匹配;
  • 动态链接错误:若未设置prefer_rlib: true,设备上可能缺少对应的动态库。这是 birthday_service/Android.bp 注释中明确提示的风险。

小结

deploy一课打通了 Rust Binder 服务从"代码"到"设备上可调用服务"的最后一公里:通过m完成 Soong 构建,用adb推送并启动birthday_server,用service check确认服务注册,用service call完成免客户端的接口级验证。整个过程背后是 AIDL 的 Rust 后端、BnBirthdayService组合包装、add_service注册与join_thread_pool线程池监听等机制在支撑——这些机制的具体实现,均可在 src/android/aidl/birthday_service 目录与 src/android/build_all.sh 中找到对应源码。跑通本教程后,你便掌握了在 AOSP 中部署任意 Rust Binder 服务的通用套路:定义接口、实现服务、编译部署、shell 验证。

【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询