30、项目实战:从零搭建 AAOS 车载信息娱乐系统、常见问题排查、性能调优案例

说实话,前面讲了那么多理论,今天终于到了我最喜欢的环节——动手干。

这一章,我会带你从零开始,搭建一个完整的 AAOS 车载信息娱乐系统。不是那种 Hello World 的玩具,而是一个能跑在模拟器上、有真实交互、能排查问题的工程。

我会把我在几个量产项目中踩过的坑、调优的经验,一股脑倒给你。你跟着走一遍,基本就能应付大部分车载开发的日常了。

30.1 搭建前的准备工作

别急着敲代码。先看看你的环境够不够硬。

项目 最低要求 推荐配置
CPU 8 核 16 核以上
内存 16 GB 32 GB 或更高
磁盘 250 GB 空闲 500 GB SSD
操作系统 Ubuntu 20.04 Ubuntu 22.04

我个人习惯用 Ubuntu 22.04 + 32GB 内存。你想想看,一次完整编译要跑 40 分钟,如果内存不够,直接 OOM 崩溃,那才叫崩溃。

注意: 我曾经在 16GB 机器上硬跑 AAOS 编译,结果 swap 撑爆,系统卡死。后来加了 16GB 交换分区才勉强跑完。所以,内存能大就大。

30.2 创建项目骨架

我们基于 AAOS 官方分支 android-14.0.0_r30 来搭建。先拉代码:

mkdir aaos-project && cd aaos-project
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r30
repo sync -j8

嗯,这里要注意。同步时间取决于你的网络。我在国内项目时,通常用清华或中科大的镜像,速度能快 3 倍。

同步完成后,我们创建一个新的设备配置。假设我们的车载设备叫 my_car

mkdir -p device/mycompany/my_car
cd device/mycompany/my_car

然后创建核心文件:

  • AndroidProducts.mk —— 定义产品名称和编译目标
  • BoardConfig.mk —— 硬件板级配置
  • device.mk —— 包含哪些模块、应用
  • system.prop —— 系统属性

举个例子,AndroidProducts.mk 长这样:

PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/my_car.mk

my_car.mk 里,我会这样写:

$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, packages/services/Car/car_product/build/car.mk)

PRODUCT_NAME := my_car
PRODUCT_DEVICE := my_car
PRODUCT_BRAND := Android
PRODUCT_MODEL := AAOS Car Demo

# 加入车载特有模块
PRODUCT_PACKAGES += \
    CarLauncher \
    CarSettings \
    CarSystemUI
小技巧: 我建议一开始只加最少模块。等基础跑通了,再一个个加。否则编译报错时,你根本不知道是哪个模块的问题。

30.3 核心架构图

下面这张图,是我自己总结的 AAOS 信息娱乐系统核心分层。你看一眼,后面所有代码都围绕这个结构展开。

应用层 CarLauncher | CarSettings | 第三方车载应用 Car Service 框架层 CarPropertyService | CarAudioService | CarHvacManager HAL 硬件抽象层 Vehicle HAL | Audio HAL | Camera HAL 内核与驱动层 Linux Kernel | CAN 驱动 | GPIO | I2C/SPI

说白了,上层应用只管 UI 和交互,中间 Car Service 负责跟车打交道,底层 HAL 跟硬件通信。每一层都有自己该干的事,别越界。

30.4 编译与烧录

配置写好了,开始编译:

source build/envsetup.sh
lunch my_car-userdebug
make -j16

我第一次编译时,等了 40 分钟,结果报了个错:No rule to make target 'vendor/xxx'。查了半天,发现是 device.mk 里引用了一个不存在的模块路径。

经验之谈: 编译报错 90% 是路径写错或模块依赖缺失。我的排查顺序是:先看 PRODUCT_PACKAGES 里的模块名对不对,再看 BoardConfig.mk 里的 TARGET_BOARD_PLATFORM 是否匹配。

编译成功后,生成的目标文件在 out/target/product/my_car/ 下。用模拟器跑的话:

emulator -avd my_car_avd -wipe-data

嗯,第一次启动会比较慢。别急,等个两三分钟,看到 CarLauncher 界面出来,就说明成了。

30.5 常见问题排查

这里我列几个我在项目中遇到的高频问题,你八成也会碰到。

30.5.1 CarService 起不来

症状:模拟器启动后黑屏,logcat 里看到 CarService: failed to start

原因:多半是 Vehicle HAL 没有正确实现。AAOS 默认需要一个 Vehicle HAL 服务,如果没有,CarService 会直接罢工。

解决方法:在 device.mk 里加上默认的模拟 HAL:

PRODUCT_PACKAGES += \
    android.hardware.automotive.vehicle@2.0-service

30.5.2 音频没声音

症状:界面正常,但播放音乐没声音。

排查:先看 dumpsys audio 里有没有输出设备。我遇到过是 audio_policy_configuration.xml 里缺少车载音频策略。

解决:在 device.mk 中引入车载音频配置:

PRODUCT_COPY_FILES += \
    device/mycompany/my_car/audio/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml

30.5.3 触摸屏没反应

症状:模拟器里鼠标点击没反应。

原因:模拟器默认没有启用触摸事件转发。需要加启动参数:

emulator -avd my_car_avd -no-audio -no-window -gpu swiftshader_indirect
注意: 我曾经在真机上调试触摸,发现驱动层上报的坐标是反的。后来在 BoardConfig.mk 里加了 BOARD_TOUCH_SCREEN_ROTATION := 180 才解决。真机调试时,先确认坐标映射。

30.6 性能调优案例

系统跑起来了,但卡顿、掉帧、启动慢,这些都是家常便饭。我分享两个真实调优案例。

30.6.1 启动速度优化

项目里有个需求:从上电到 CarLauncher 显示,不能超过 8 秒。一开始测出来是 14 秒。

我排查后发现,瓶颈在 SystemServer 里加载了太多不必要的服务。比如车载根本用不到 TelephonyBluetooth 的某些模块。

优化方法:在 system.prop 里禁用非必要服务:

ro.car.startup.disable_telephony=true
ro.car.startup.disable_bluetooth_profile=true

配合 zygote 预加载优化,最终启动时间降到了 7.2 秒。

30.6.2 界面掉帧问题

另一个项目,导航地图滑动时掉帧严重。我用 systrace 抓了一下,发现是 SurfaceFlinger 的 vsync 周期被音频线程打断了。

说白了,就是音频 HAL 占用了太多 CPU 时间,导致 UI 渲染线程抢不到时间片。

解决方案:把音频处理线程绑定到小核上,UI 线程绑定到大核:

# 在 init.rc 中设置
on boot
    write /dev/cpuset/foreground/cpus 4-7
    write /dev/cpuset/audio/cpus 0-3

调完后,掉帧率从 15% 降到了 2% 以下。

小技巧: 性能调优时,我习惯先用 systrace 看整体,再用 perf 定位热点函数。别一上来就瞎猜,数据说话。

30.7 小结

这一章,我们从零搭了一个 AAOS 信息娱乐系统。你经历了环境准备、项目创建、编译烧录、问题排查、性能调优的全流程。

说实话,车载开发跟手机开发最大的区别,就是你要跟硬件、跟车打交道。很多问题在模拟器上跑得好好的,一上真车就翻车。但别怕,只要掌握了排查思路和调优方法,大部分坑都能填平。

嗯,今天就到这里。你动手试试,有问题随时翻回来看。


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