ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AOSP源码同步与vendor分区解析:从repo到QSSI的完整指南

AOSP源码同步与vendor分区解析:从repo到QSSI的完整指南 第一次接触 AOSP 的工程师多半被两个东西劝退过一个是.repo另一个是vendor。前者让你怀疑自己是不是在用 yum 配源后者让你在系统启动日志里反复看到vd is starting, please check vendor daemons status in debug log然后一脸懵。其实这两件事贯穿了 Android 从源码到镜像的整条链路搞懂它们就等于拿到了阅读这套系统“生命蓝图”的索引。这篇文章我打算站在一个常年和 AOSP 打交道的工程师视角把.repo同步、源码目录结构、vendor分区、QSSI 依赖、构建烧录以及“去掉感叹号”这类经典需求串起来讲。不画大而全的架构图只讲你实际操作时会用到的命令、配置和排查思路。如果你正准备定制 ROM、做 framework 开发或者刚进入 BSP 领域这篇应该能帮你少走不少弯路。1. AOSP全景为什么先看 .repo 和 vendor1.1 从“施工台账”说起AOSP 不是一个 git 仓库而是几百个 git 仓库的集合。.repo目录虽然是隐藏目录但你一进源码根目录第二眼看到的基本就是它。如果把整个 Android 系统比作一座城市.repo就是施工总台账它记录了这个城市有哪些分标段、每个标段对应哪个 git 仓库、当前应该停在哪个版本、往哪个远程地址同步。很多新人会犯一个低级错误把repo命令和 Linux 的repo源混为一谈。搜索cannot find a valid baseurl for repo: base/7/x86_64出来的全是 CentOS 的 yum 源配置问题和 Android 开发毫无关系。AOSP 里的repo是 Google 用 Python 写的一个仓库管理工具不是“软件源”。你只需要确保repo命令在 PATH 中可用然后通过repo init读取一份 manifest它就会按清单把整个项目拉下来。1.2 一张系统镜像里的“活地图”AOSP 源码按功能分成几大区域build目录是构建脚本和编译规则frameworks是 Java 层和本地服务hardware是硬件抽象层的参考实现packages是系统应用system里放着一个个核心进程如system_servervendor和device则属于各家芯片厂商和终端厂商的适配代码。站在编译产物的角度看这些源码最终会被组织成system.img、vendor.img、boot.img、dtbo.img等镜像。system.img承载操作系统公共部分vendor.img承载硬件相关部分。从 Android 8.0 开始Google 强推 Project Treble把system和vendor分区彻底解耦目的就是让系统升级不再依赖厂商更新 vendor 分区。于是“vendor”从一个目录名词变成了一个架构概念后面几乎所有启动问题、兼容性问题和它有关。1.3 一条完整的“生命蓝图”链路要像工程师一样读懂 AOSP建议按这个顺序走一遍先通过.repo拿到源码再理解system和vendor的边界接着看构建系统如何把代码映射成镜像最后在设备上通过日志、属性、调试工具反推运行时行为。可以把这条链路理解为“静态源码—构建产物—动态启动”的循环。我遇到很多人一上来就盯着某个 framework 文件看结果改了半天发现自己连vendor目录都没同步编译时又是缺这个缺那个。实际上AOSP 的“生命”不只存在于 Java 代码里还存在于.rc文件、SELinux 策略、VINTF manifest 和一堆Android.bp中。只有把.repo和vendor这两块地基打牢后面读代码、改系统才不会像无头苍蝇。2. repo实操从零同步一套 AOSP 源码2.1 先分清楚 repo 和 yum 源我第一次看见cannot find a valid baseurl for repo: base/7/x86_64时也愣了一下因为那行报错里确实有 “repo” 这个关键字。实际上这是 CentOS 的/etc/yum.repos.d/配错了源导致yum找不到基础仓库。它和 Android 的repo工具是两个东西。确认你用的是哪个 repo 很简单which repo repo version如果输出类似repo version v2.35那才是 AOSP 要用的工具。如果没有安装repo可以用apt-get install repo或者从 AOSP 官方渠道包一份回来。国内用户通常使用清华源或中科大源同步记得在repo init时把 URL 指向镜像地址否则全量同步的时间和稳定性都很感人。2.2 初始化清单并同步AOSP 的 manifest 通过一个 XML 文件描述所有 git 仓库的位置、分支和校验信息。初始化一个工程分两步mkdir aosp cd aosp repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-13.0.0_r75-u指定 manifest 仓库-b指定分支。Android 版本分支一般形如android-13.0.0_r75不同 patch 版本对应不同 tag。完成后repo sync会按照 manifest 把所有仓库同步到本地repo sync -c -j8-c表示只同步当前分支-j8是并发数。网络不太好的机器建议写个重试脚本循环执行repo sync直到没有错误。我实际用下来第一次全量同步的时间大概取决于磁盘速度和网络磁盘建议至少留 300GB代码加编译产物很容易超过 150GB。2.3 用 local manifest 加入私有仓库不管是做 ROM 定制还是芯片适配你大概率会在 AOSP 基础上加入自己的设备仓库或 framework 仓库。做法是新建.repo/local_manifests/目录然后添加一个 XML?xml version1.0 encodingUTF-8? manifest remote namemyremote fetchhttps://git.example.com/android/ / project pathdevice/mycompany/mydevice namedevice_mycompany_mydevice remotemyremote revisionmaster / /manifestpath表示源码根目录下的相对路径name是远程仓库名revision可以指定分支或 tag。修改完再跑repo sync自己的仓库就会被拉进来。这样既不影响官方 manifest也能长期保持本地定制。2.4 多仓库分支管理的入门手法AOSP 仓库太多逐个项目切分支不现实。repo提供了一组批量操作命令repo start myfeature --all repo forall -c git status repo forall -c git checkout mybranchrepo start会在所有项目上创建同一个本地分支适合管理一个大 feature。repo forall -c则是在每个项目上执行任意 shell 命令非常方便批量查看状态。想同步所有仓库到最新版但不想全量重来可以repo sync -c -j8 -q-q安静模式只输出错误和关键信息屏幕不会刷到眼花。这个习惯建议早点养成不然每次 sync 都像在看弹幕。3. vendor分区拆解Treble时代的主线剧情3.1 为什么要单独分一个 vendor早期的 Android 所有东西都塞在 system 分区Google 每次升级系统厂商都要跟着适配否则底层硬件驱动不兼容。Project Treble 把硬件相关代码从系统框架中剥离出来放入独立的 vendor 分区这样system.img和vendor.img可以独立升级。从源码目录看vendor有时候是芯片厂商的适配目录编译后对应/vendor镜像里的内容。它包含 HAL 库、硬件抽象服务、底层配置、固件、甚至一些硬件相关的系统服务。启动阶段init 进程会先挂载 system 和 vendor然后逐个启动 vendor 里的 daemon 进程。如果 vendor 镜像和 system 镜像不匹配轻则某个硬件功能不可用重则启动循环。3.2 那个 “vd is starting” 到底在说什么热词里有一条很经典的日志vd is starting, please check vendor daemons status in debug log。这段提示通常出现在早期调试输出中意思是 vendor daemon 已经开始启动如果后续有问题请去完整 debug log 里查看状态。对新手来说最难判断的是它到底是正常打印还是暗示启动失败我的经验是只要系统能正常开机这句话基本上就是一个普通状态节点。但如果它后面跟着一堆vndservicemanager、hwservicemanager的异常或者卡在这里几分钟不动那就要认真排查了。推荐按这个顺序查adb shell ps -A | grep vndservicemanager adb shell ps -A | grep hwservicemanager adb logcat -b all -d | grep -E vndservicemanager|vendor|SELinux adb shell dmesg | grep -i vndvndservicemanager是 vendor 分区的 Binder 服务注册中心厂商进程通过它注册自己的服务。它起不来很多 HAL 服务就找不到 provider。常见原因是 SELinux deny 或者某些.rc文件里服务名写错。如果日志里有avc: denied字样先去看 SELinux 策略不要急着改代码。3.3 往 vendor 分区加文件并不是复制粘贴开发阶段往/vendor里塞一个二进制固件看起来很简单PRODUCT_COPY_FILES \ device/mycompany/mydevice/firmware/foo.bin:vendor/firmware/foo.bin但上生产后你会发现光是SELinux label不对固件就没法读取。vendor 分区的文件需要配置对应的file_contexts否则文件 label 不是期望的vendor_file服务的 io 操作会被拒绝。另外动态库的依赖关系也要小心一个 vendor 下的二进制依赖了 system 下的 .so如果那个 .so 不是 VNDK启动时直接cannot locate symbol。我在给某个设备适配外设时遇到过把.so拷贝进去但忘了改 SELinux label服务反复重启logcat 里只看到 “Permission denied”。最后用restorecon修复上下文才通过。这个教训说明vendor 分区不是简单的文件堆砌而是一个有自己的权限体系、依赖体系和开机流程的子系统。4. QSSI与vendor依赖那些让你抓狂的.so4.1 高通 QSSI 机制和 vendor 的关系如果你是做高通平台一定见过qssi这个缩写。它的全称是 Qualcomm Single System Image是高通为了配合 Project Treble 推出的系统镜像方案。简单理解qssi 是 system 侧vendor 是硬件侧两个代码仓库独立构建最终在设备上组合。这就引出了热词里那个问题qssi 与 vendor 两个代码需要依赖 qssi 里面的 so怎么办比如 vendor 分区的 camera 进程需要链接libmmcamera_interface.so而这个库在 qssi 的 system 分区里。如果你直接在 vendor 的Android.bp里加入shared_libs: [libmmcamera_interface.so]编译时很可能报错运行时也可能因为README找不到符号而崩溃。4.2 先确认依赖方向对不对在动手前先用readelf看依赖关系readelf -d out/target/product/xxx/vendor/bin/camera.provider | grep NEEDED如果依赖的库被标记为 LL-NDK 或 VNDK-SP那 vendor 引用它没问题。如果你的库不是 VNDK就属于“私有依赖”系统不允许跨分区随便链接否则会破坏 Treble 的兼容边界。解决办法通常有几种把库提升为vendor_available并在 VNDK 列表中加入它。在依赖方把该库也打包到 vendor 分区通过PRODUCT_PACKAGES libfoo实现。修改Android.bp让库在 system 和 vendor 各生成一个副本。最稳妥的架构方案是把真正需要跨分区调用的逻辑封装成 HIDL/AIDL 服务vendor 作为 client 通过接口访问 system 服务而不是直接链.so。虽然前期工作量大但后面升级和维护会轻松很多。4.3 VNDK 和 LL-NDK 简介VNDKVendor Native Development Kit是 Google 允许 vendor 分区使用的 system 分区库集合。它包含 LL-NDK 和 VNDK 库。列表通常由build/soong/cc/config/下的vndk.py等文件维护。想查看当前分支的 VNDK 版本get_build_var VNDK_VERSION在Android.bp里一个库想对 vendor 可见至少需要设置vendor_available: true, vndk: { enabled: true, },编译时再检查是否需要生成 VNDK snapshot。如果你对这段不熟建议先跑一个完整的 AOSP build在out/soong/.vndk目录里翻一翻能直观看到当前设备可用的 VNDK 库清单。5. 构建、烧录与去感叹号把改动变成镜像5.1 从 lunch 到 make 的常见姿势编译 AOSP 说简单也简单说难也难。简单在于官方流程固定source build/envsetup.sh lunch product-userdebug make -j$(nproc)难点在于你选的目标产品是否正确、分支是否匹配、依赖是否齐全。lunch的输出会告诉你当前使用哪个 device 配置比如qssi-userdebug和bengal-userdebug对应不同产品。如果单独开发 qssi可能需要先构建 qssi 镜像再单独构建 vendor 镜像最后用工具把它们拼成刷机包。5.2 开启 ccache 和并行构建AOSP 全量编译一次动辄几小时没点加速手段根本等不起。建议配置 ccache 缓存export USE_CCACHE1 export CCACHE_MAXSIZE50G ccache -M 50G如果机器内存够大make -j16甚至-j32能显著缩短时间。但注意内存不够时会出现 OOM进程被直接杀掉所以不是线程数越大越好。我一般先看free -h估算每个编译进程大概吃 2GB 内存再决定-j的值。另外out目录建议放在 SSD 上机械硬盘编译 AOSP 真的会让人怀疑人生。5.3 快速烧录到模拟器或真机不用真机调试时emulator是效率很高的选择。在make完成后直接运行emulator -avd name -no-snapshot -no-window如果用真机fastboot刷机时最常用的命令是fastboot flashall -w也可以只刷指定分区fastboot flash system out/target/product/xxx/system.img fastboot flash vendor out/target/product/xxx/vendor.img fastboot reboot强调一下刷机前备份非易失数据尤其是 bootloader、vbmeta 等项目。我不止一次见过同事误刷了 bootloader 导致变砖最后只能拆机短接救砖。5.4 去掉状态栏感叹号网络检测那点事“AOSP 去除感叹号”这个需求几乎每个 ROM 定制者都遇到过。它本身和 vendor 没关系但同样属于源码级改动。感叹号的来源是系统在联网后做 captive portal 检测向 Google 的connectivitycheck.gstatic.com发请求请求失败就认为网络“不可信”或“未联网”于是左上角 wifi/数据图标冒出感叹号。临时验证最简单adb shell settings put global captive_portal_detection_enabled 0然后飞行模式切换一下感叹号就没了。但做 ROM 的时候更优雅的方式是在源码里改默认值。通常修改frameworks/base/packages/SettingsProvider/res/values/defaults.xml里和captive_portal_*相关的配置或者通过overlay覆盖。如果你想保留检测只是换一个国内可访问的检测服务器可以修改adb shell settings put global captive_portal_http_url http://connect.rom.miui.com/generate_204 adb shell settings put global captive_portal_https_url https://connect.rom.miui.com/generate_204这个思路在很多国产 ROM 里都能看到。记住关了检测会影响系统判断当前网络是否需要登录所以做产品时要谨慎。6. 常见问题排查与避坑实录6.1 AOSP 日常问题速查表下面这张表是我在项目里反复用到的高频问题整理遇到相似情况可以照着试。现象可能原因排查方向repo sync中途卡住或失败网络断流、git 对象损坏使用repo sync -c -j8 -q写循环重试脚本必要时rm -rf .repo/projects内对应仓库重新拉取错误cannot find a valid baseurl for repo这是 CentOS yum 源配置错误不是 AOSP 的 repo 工具检查/etc/yum.repos.d/与 Android 源码同步问题无关启动日志出现vd is starting, please check vendor daemons status in debug log正常状态提示但伴随 vendor daemon 异常需排查用ps -A检查vndservicemanager、hwservicemanager再查 logcat、dmesgvendor 进程崩溃报cannot locate symbolvendor 二进制依赖了非 VNDK 共享库用readelf -d查看 NEEDED确认库是否属于 VNDK或把库放入 vendor 分区编译过程中进程被 OOM kill内存不足并行任务过多降低-j参数增大 swap或关闭桌面环境释放内存修改了 framework 代码编译后没生效增量构建没有重新触发相关模块用m framework或精确到mmm frameworks/base单独模块编译Android Studio Hedgehog 2023.1.1 Patch 2 是否支持 AGP 8取决于版本匹配一般支持 AGP 8.0~8.2查看 AGP 官方版本矩阵必要时降低项目 AGP 版本6.2 排查 vendor daemon 的完整示例有一次我在调试开机卡在 logo 的问题日志里反复出现vd is starting, please check vendor daemons status in debug log但没有更多提示。一开始以为只是普通打印后来发现进入系统后相机、声音全不可用。我当时的排查步骤是这样adb shell ps -A | grep -E vnd|vendor adb shell dmesg | grep -i avc\|denied adb logcat -b crash -d adb logcat -b system -d | grep -E vndservicemanager|hwservicemanager结果发现是某个 vendor 服务因为 SELinux policy 缺了一条允许规则导致无法向vndservicemanager注册。修复方式是在设备相关sepolicy/vendor目录里补上一个allow规则重新编译 vendor 镜像。如果你是在 emulator 上看到类似问题先确认 system image 和 vendor image 是不是来自同一个构建。AOSP 的模拟器 image 如果混用了不同分支的 system 和 vendor很容易在 vendor daemon 阶段翻车。6.3 用 repo forall 批量救急代码跨几十个仓库做修改后最怕某个仓库没提交或落后版本。我习惯用这一组命令快速摸清全局状态repo forall -c git status repo forall -c git log --oneline -5 repo forall -c git diff HEAD~1 --stat比如要批量把所有仓库切到同一个 tagrepo forall -c git checkout android-13.0.0_r75但这命令要慎用因为有些仓库可能自己领先或落后直接 checkout 会丢改动。建议先git status看看有没有未提交的本地变更再统一操作。6.4 关于 Android Studio 和 AGP 版本的小提醒热词里有人问android studio hedgehog | 2023.1.1 patch 2支持agp8版本吗。这个问题和 AOSP 关系不大但开发 framework 时经常要回到 Android Studio 工程里看代码。Android Studio Hedgehog 2023.1.1 对 AGP 8 的支持基本没问题但 AGP 版本太新时会提示升级 IDE。如果你手头项目用 AGP 8.0Hedgehog 是完全能跑的如果项目切到 AGP 8.3建议至少使用 Koala 或更新版本避免构建脚本报一堆莫名错误。对 AOSP 开发来说其实大部分文件用 VSCode 或者vim cscope也够了Android Studio 更适合看 framework Java 层和写系统应用。别把宝贵时间花在折腾 IDE 兼容性上能跑代码、能跳转、能搜符号就够用了。最后再分享一个小技巧调试 AOSP 时很多人一上来就反复刷整机镜像其实效率很低。我的习惯是尽量拆分成最小可验证单元改 framework 就先编 framework 模块改 vendor 服务就先编 vendor 的二进制再把产物推到对应分区或 adb push 到/system、/vendor对应路径。Android 的userdebug版本支持 adb root先把修改后的 so 推到/system/lib64或/vendor/lib64重启进程看是否生效能省下大量烧写等待时间。当然开发阶段临时 push 是捷径最终发布前一定要走完整镜像构建确保分区权限和 SELinux 上下文都正确。能把.repo管得明明白白、把 vendor 依赖梳理得清清楚楚AOSP 这套“生命蓝图”对你来说就基本没有死角了。
返回列表