系统集成与发布:从代码到产线的最后一公里

各位同学,今天我们来聊聊整个移植过程中最“接地气”的一章——系统集成与发布。说白了,就是把咱们前面辛辛苦苦移植好的代码,变成能烧到板子上、能跑起来、能交给工厂批量生产的完整系统镜像。

我个人习惯把这一章叫做“临门一脚”。代码写得再好,如果最后打包出来的镜像有问题,或者产线烧录效率太低,那前面的工作基本白干。我在项目里见过太多次,开发板跑得飞起,一到产线就各种翻车。嗯,今天咱们就把这些坑提前填上。

核心要点:系统集成不是简单的“make”一下完事,它涉及Build系统定制、分区规划、镜像打包、工厂烧录和产测五个环节。每个环节都有门道。

一、Build系统定制:别用默认配置直接上

Android的Build系统,说白了就是一套基于Makefile和Soong的构建框架。但咱们做i.MX平台移植,不能直接用AOSP的默认配置。为什么?因为硬件平台不同,驱动、HAL、设备树都不一样。

我建议你从这几个地方入手:

  • BoardConfig.mk:定义板级配置,比如分区大小、内核路径、bootloader参数。我习惯把分区大小写死在这里,避免后续镜像超限。
  • device.mk:包含产品相关的模块列表,比如要编译哪些HAL、哪些服务。
  • AndroidProducts.mk:声明产品名称和配置文件路径。这里容易踩坑——产品名不要带空格和特殊字符,产线工具会报错。
# 一个典型的BoardConfig.mk片段
TARGET_BOARD_PLATFORM := imx8m
TARGET_NO_BOOTLOADER := false
BOARD_BOOTIMAGE_PARTITION_SIZE := 33554432   # 32MB
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1073741824  # 1GB
BOARD_USERDATAIMAGE_PARTITION_SIZE := 2147483648  # 2GB

小技巧:我在做i.MX8M项目时,发现默认的system分区只有512MB,结果GMS包都塞不下。后来我提前算好所有预装APK的大小,把分区改到1.2GB才搞定。建议你也在BoardConfig里预留20%的余量。

二、产品分区规划:别等镜像装不下才后悔

分区规划,说白了就是给系统各个部分分配存储空间。Android Automotive的分区比手机版更复杂,因为要放车机专属数据。

典型的i.MX Android Automotive分区表长这样:

分区名 大小 用途
boot 32MB 内核+ramdisk
system 1GB~2GB 系统文件
vendor 512MB 厂商HAL和驱动
product 512MB 产品定制APK
userdata 剩余空间 用户数据
cache 256MB OTA缓存

这里有个坑——vendor分区和product分区。Android 10以后强制要求分离,但很多i.MX的BSP默认还是合在一起的。我曾经因为没改分区表,导致vendor镜像写爆,整台车机变砖。嗯,后来我学乖了,每次改分区前先跑一遍df -h看看实际占用。

警告:分区大小一旦定下来,后期改起来非常麻烦。尤其是userdata分区,如果产线已经烧录了第一批设备,再改分区表意味着要重新格式化所有存储芯片。所以前期规划一定要慎重。

三、系统镜像打包:make命令背后的门道

打包镜像,大家都会敲make -j8。但你真的知道它背后干了什么吗?

简单说,Android的打包流程分三步:

  1. 编译:把源码编译成二进制文件、APK、JAR包。
  2. 组装:把编译产物按分区目录结构放好。
  3. 生成镜像:用mkuserimgbuild_image工具生成ext4或sparse格式的镜像文件。

我个人习惯在打包前加一个make clean,虽然慢一点,但能避免增量编译带来的奇怪问题。有一次我偷懒没clean,结果vendor分区里残留了一个旧版本的HAL,导致触摸屏失灵。排查了整整两天才找到原因。

# 打包命令示例
source build/envsetup.sh
lunch evk_8mp-userdebug
make -j8 2>&1 | tee build.log

# 打包完成后,镜像在 out/target/product/evk_8mp/ 下
ls -lh out/target/product/evk_8mp/*.img

避坑指南:我曾经遇到过打包出来的system.img比分区大,烧录时直接报错。后来发现是APK里塞了太多so库。建议你在Android.mk里用LOCAL_MODULE_TAGS := optional来控制哪些模块进系统镜像,别一股脑全编进去。

四、工厂烧录:效率与稳定性的平衡

工厂烧录,是量产环节最头疼的事。开发板烧录可以用fastboot,但产线上不可能一台一台手动敲命令。

常用的烧录方式有两种:

  • SD卡烧录:把镜像放到SD卡里,板子从SD卡启动后自动烧录到eMMC。适合小批量。
  • USB烧录:用NXP的UUU工具(Universal Update Utility),通过USB把镜像推送到板子。适合大批量。

我推荐用UUU,因为它支持脚本化。你可以写一个uuu.auto脚本,把烧录流程自动化:

# uuu.auto 脚本示例
uuu_version 1.0.0

# 烧录bootloader
SDP: boot -f flash.bin
SDPV: delay 1000
SDPV: write -f u-boot.imx
SDPV: jump

# 烧录分区镜像
FB: ucmd setenv fastboot_dev mmc
FB: flash bootloader flash.bin
FB: flash boot boot.img
FB: flash system system.img
FB: flash vendor vendor.img
FB: flash product product.img
FB: flash userdata userdata.img
FB: flash cache cache.img

# 重启
FB: ucmd reset

注意:产线烧录时,USB线缆质量很重要。我曾经遇到一批设备烧录到一半失败,排查发现是USB线太长导致信号衰减。后来统一换成1米以内的屏蔽线,问题解决。另外,烧录工位最好配UPS,断电一次整条线都得重来。

五、产测:别让坏板子流出工厂

产测,就是生产测试。烧录完系统后,得验证硬件和软件是否正常工作。车机不比手机,出了问题可能涉及安全。

我一般把产测分成两个阶段:

  1. 硬件自检:上电后跑一段裸机程序,检测DDR、eMMC、网卡、CAN总线等关键硬件。这个阶段不依赖Android系统。
  2. 系统功能测试:Android启动后,用自动化脚本测试触摸屏、蓝牙、Wi-Fi、GPS、音频等。

这里分享一个我踩过的坑:有一批设备烧录后触摸屏没反应,产测报告全是PASS。后来发现是产测脚本只检查了触摸屏驱动是否加载,没检查实际触控坐标上报。从那以后,我要求产测必须模拟真实操作——比如画一条线,看坐标是否连续。

产测脚本核心逻辑:

# 检查触摸屏
adb shell getevent -c 10 /dev/input/event1 | grep "ABS_MT_POSITION"
if [ $? -eq 0 ]; then
    echo "Touch: PASS"
else
    echo "Touch: FAIL"
fi

# 检查Wi-Fi
adb shell iw dev wlan0 link | grep "Connected"
if [ $? -eq 0 ]; then
    echo "Wi-Fi: PASS"
else
    echo "Wi-Fi: FAIL"
fi

知识体系总览

下面这张图,我把整个系统集成与发布的流程串起来了。你可以把它贴在工位旁边,每次做集成时对照着检查。

系统集成与发布流程 Build定制 分区规划 镜像打包 工厂烧录 产测验证 发现问题 → 返回Build定制修改 BoardConfig.mk device.mk 产品名定义 boot/system/vendor product/userdata cache分区 make -j8 mkuserimg sparse格式 SD卡烧录 UUU工具 自动化脚本 硬件自检 + 功能测试

这张图里,我特意画了一条从产测回到Build定制的虚线。为什么?因为产测发现的问题,往往需要回到Build系统去修改——比如某个驱动没编进去,或者分区大小不够。这个闭环,是量产项目里最常见的迭代模式。


公众号:蓝海资料掘金营,微信deep3321