4. Android Automotive OS框架解析:CarService架构、多用户与多显示策略、AAOS的渲染管线(SurfaceFlinger)

好,咱们今天聊聊AAOS的核心。说实话,很多做座舱HMI的朋友,一开始接触AAOS都会有点懵——这玩意儿跟手机Android到底差在哪?我当年刚转过来的时候,也花了小半个月才理清楚脉络。今天我就把几个最关键的模块掰开揉碎了讲给你听。

4.1 CarService架构:座舱的“大脑”

CarService是什么?说白了,它就是AAOS里专门管汽车硬件的那一层。你想想看,手机Android不需要管车窗、空调、方向盘按键吧?但车机需要。Google的做法很聪明——在标准Android框架之上,加了一层CarService。

核心思路:CarService作为系统服务运行,通过Car API向上层HMI应用提供车辆功能接口。它内部通过Vehicle HAL与底层硬件通信。

我习惯把CarService拆成三层来看:

  • Car API层——给HMI应用调用的Java接口,比如Car.createCar()Car.getCarInfo()。应用开发者只需要关心这些API,不用管底层怎么跟CAN总线打交道。
  • CarService服务层——核心逻辑所在。管理车辆属性(Vehicle Property)、处理策略(比如车速超过20km/h时自动锁定某些功能)。
  • Vehicle HAL层——硬件抽象层。各家OEM自己实现,把CAN信号、以太网信号映射成AAOS能理解的Vehicle Property。

我在项目中遇到过一个问题:某款车型的空调控制延迟特别严重,按下去要等1秒才有反应。查了半天,发现是CarService里某个属性订阅的回调处理太慢,阻塞了UI线程。嗯,这里要注意——CarService的回调默认跑在binder线程池里,千万别在里面做耗时操作。

避坑指南:我曾经因为没注意Vehicle Property的订阅频率,导致CarService CPU飙升到80%。后来加了去抖逻辑,每100ms只处理一次属性变化,问题就解决了。

4.2 多用户与多显示策略:一车多屏的挑战

现在的座舱,哪个不是三块屏起步?仪表、中控、副驾屏,甚至还有后排娱乐屏。AAOS怎么管这么多屏幕?答案是——多用户+多显示策略。

4.2.1 多用户策略

AAOS支持同时运行多个用户会话。什么意思?就是驾驶员登录自己的账号,副驾登录另一个账号,两个人在同一台车机上用不同的配置。这在手机Android上是做不到的。

我建议你这样理解:

  • User 0——系统用户,跑CarService等系统服务。
  • User 10——当前前台用户,通常是驾驶员。跑HMI应用。
  • User 11+——其他用户,比如副驾或后排乘客。每个用户有自己的应用栈和设置。

多用户带来的好处很明显:副驾可以切歌、调空调,完全不影响驾驶员的操作。但坑也不少。我记得有一次,副驾屏的导航应用崩溃了,结果整个系统的焦点都乱了,驾驶员那边的语音助手也失灵了。后来发现是用户切换时,ActivityManager的焦点管理没处理好。

注意:多用户环境下,每个用户的SurfaceFlinger缓冲区是独立的。如果两个用户同时渲染高帧率动画,GPU带宽可能会成为瓶颈。我曾经在某个项目里,副驾屏播放视频时,仪表盘的帧率直接从60fps掉到了30fps。

4.2.2 多显示策略

AAOS通过DisplayManager管理多个物理屏幕。每个屏幕可以绑定到不同的用户,也可以绑定到同一个用户的不同窗口。核心配置在display_policy.xml里。

举个例子,一个典型的三屏配置:

<display-policy>
    <display id="0" type="INTERNAL">
        <layer-stack>0</layer-stack>
        <primary>true</primary>
    </display>
    <display id="1" type="EXTERNAL">
        <layer-stack>1</layer-stack>
        <primary>false</primary>
    </display>
    <display id="2" type="EXTERNAL">
        <layer-stack>2</layer-stack>
        <primary>false</primary>
    </display>
</display-policy>

这里每个layer-stack对应一个独立的合成通道。说白了,就是每个屏幕有自己的图层栈,互不干扰。但要注意,SurfaceFlinger在处理多个layer-stack时,如果硬件合成器(HWC)不支持那么多通道,就会回退到GPU合成——性能直接崩掉。

我的经验:做多屏项目时,一定要先确认HWC支持多少个硬件合成层。如果只有2个,你非要搞3个独立屏幕,那就等着掉帧吧。我一般会留一个屏幕走GPU合成,其他走HWC,这样性能最均衡。

4.3 AAOS的渲染管线:SurfaceFlinger深度解析

SurfaceFlinger,这个名字你肯定不陌生。它是Android图形系统的核心,负责把所有应用的图层合成到一起,然后输出到屏幕。在AAOS里,它的角色更重——因为要同时处理多个屏幕、多个用户、多个窗口。

4.3.1 渲染管线流程

我习惯把SurfaceFlinger的渲染管线分成四个阶段:

  1. Buffer提交——应用通过Surface提交渲染好的Buffer(通常是GraphicBuffer)。每个Buffer对应一个图层。
  2. 合成决策——SurfaceFlinger决定哪些图层用硬件合成(HWC),哪些用GPU合成(GLES)。决策依据是图层的数量、大小、透明度等。
  3. 合成执行——HWC合成的话,直接交给显示硬件;GPU合成的话,SurfaceFlinger自己用OpenGL ES把图层画到一起。
  4. 显示输出——合成后的Buffer送到显示控制器,刷新到屏幕。

为什么会掉帧?说白了,就是某个阶段卡住了。比如Buffer提交太慢,或者合成决策太复杂,或者GPU忙不过来。

关键指标:SurfaceFlinger的帧率稳定在60fps,意味着每个阶段的耗时不能超过16.6ms。如果某个阶段超过这个阈值,就会出现掉帧。

4.3.2 多屏下的SurfaceFlinger优化

我在项目中遇到过最头疼的问题:三屏同时渲染导航动画,SurfaceFlinger的CPU占用率飙到40%。后来怎么解决的?三个优化点:

  • 减少图层数量——每个屏幕的图层数控制在8层以内。超过8层,HWC大概率会回退到GPU合成。
  • 使用BufferQueue的异步模式——应用提交Buffer时,不要等SurfaceFlinger处理完再提交下一个。用异步模式,让BufferQueue自己管理。
  • 合理设置帧率——仪表盘只需要30fps,中控屏60fps,副驾屏如果只是显示静态信息,15fps就够了。别让所有屏幕都跑60fps,那是浪费。

警告:千万不要在SurfaceFlinger的主线程里做任何耗时操作。我曾经见过有人把日志打印放在合成循环里,结果帧率直接掉到20fps。SurfaceFlinger的日志应该用异步方式输出。

4.3.3 实战:用dumpsys监控SurfaceFlinger

我平时排查性能问题,最常用的命令就是dumpsys SurfaceFlinger。它能告诉你每个图层的状态、Buffer的提交情况、合成方式等。

举个例子,看看有没有掉帧:

adb shell dumpsys SurfaceFlinger | grep "Frames"
Frames: Total=1200, Dropped=3, HWC=1150, GPU=50

如果Dropped帧数占比超过1%,那就得查查了。再看看每个图层的合成方式:

adb shell dumpsys SurfaceFlinger | grep "Layer"
  Layer 0x7a12345678 (com.android.car/com.android.car.CarActivity)
    activeBuffer=[1920x1080:1088, 1]
    hwcLayer=1
  Layer 0x7a12345679 (com.android.systemui/com.android.systemui.SystemUIActivity)
    activeBuffer=[1920x1080:1088, 1]
    hwcLayer=0

hwcLayer=1表示走硬件合成,hwcLayer=0表示走GPU合成。如果发现某个关键图层走了GPU合成,那就得看看是不是图层属性(比如透明度、圆角)导致HWC不支持。

小技巧:我习惯在开发阶段,给每个应用设置不同的图层颜色。这样在dumpsys输出里,一眼就能看出哪个图层是谁的。调试效率高很多。

4.4 总结一下

AAOS的框架设计,说白了就是为汽车场景量身定做的。CarService管硬件,多用户管账号,多显示管屏幕,SurfaceFlinger管渲染。每个模块单独看都不复杂,但组合在一起,坑就多了。

我个人觉得,做座舱HMI开发,最重要的不是背API,而是理解这些模块之间的协作关系。比如CarService的某个属性变化,怎么影响到SurfaceFlinger的图层合成?多用户切换时,BufferQueue怎么重新分配?这些才是真正考验架构能力的地方。

嗯,今天就先聊到这儿。下一章咱们讲讲AAOS的窗口管理——WindowManager在座舱场景下的特殊处理,那又是一个大坑。