3. Android系统架构

各位同学,今天我们来聊聊车载Android系统定制的硬核部分。说实话,很多做应用层的朋友一听到「源码下载编译」就头大,觉得那是底层工程师的事。但在我做车载座舱系统的这些年里,踩过的坑告诉我——不懂系统定制,你永远只能做「能用」的产品,做不出「好用」的座舱。

这一章,我会带你走一遍AOSP的下载、编译流程,然后深入车载系统服务的定制方法,最后聊聊系统属性配置那些容易被忽略的细节。嗯,都是实战中反复验证过的东西。

3.1 AOSP源码下载与编译:从零搭建你的车载系统基线

先说说源码下载。你可能会问:「不就是git clone吗?有什么好讲的?」

我刚开始也这么想。直到有一次,我在项目里下载了整整两天,结果发现分支选错了,所有车载相关的包都没有……那种感觉,你懂的。

核心要点:车载Android系统定制,必须基于 android-12.0.0_rXX 或更新的版本,因为从Android 12开始,Google正式引入了 car_service 作为系统级服务。

3.1.1 环境准备与Repo初始化

AOSP用的是Repo工具来管理上百个Git仓库。我个人习惯在Ubuntu 20.04 LTS上做开发,内存至少16GB,磁盘建议留出300GB以上——别问我怎么知道的,有一次编译到一半磁盘满了,那叫一个绝望。

# 安装Repo
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

# 创建工作目录
mkdir ~/aosp_car
cd ~/aosp_car

# 初始化指定分支(以android-12.0.0_r32为例)
repo init -u https://android.googlesource.com/platform/manifest -b android-12.0.0_r32

# 同步源码(-j4表示4线程下载,根据你的网络调整)
repo sync -j4 -c --no-tags

我的经验:国内网络环境下,建议使用清华或中科大的AOSP镜像站。把 manifest 地址换成 https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest,速度能快3-5倍。

3.1.2 车载产品编译配置

源码下载完成后,关键一步是选择正确的编译目标。车载系统不是手机,我们需要的是 car 开头的产品配置。

# 加载编译环境
source build/envsetup.sh

# 查看所有可用的car目标
lunch | grep car

# 选择车载模拟器目标(最常用)
lunch aosp_car_x86_64-userdebug

# 开始编译(-j16根据你的CPU核心数调整)
make -j16

编译时间取决于机器配置。我用的工作站是32核64GB,第一次全编大概40分钟。如果你用笔记本,建议做好过夜的准备。

编译目标 适用场景 说明
aosp_car_x86_64-userdebug 模拟器开发调试 最常用,带root权限
aosp_car_arm64-userdebug 真机(高通/瑞芯微) 需要对应BSP支持
aosp_car_x86_64-user 正式发布版本 去掉调试功能,性能更优

注意:千万不要在 user 版本上做开发调试,没有root权限,很多系统服务改完没法验证。我曾经因为这个浪费了整整一天。

3.2 车载系统服务定制:从CarService到自定义Service

编译完系统,接下来就是重头戏——定制车载系统服务。说白了,车载座舱和手机最大的区别就是:手机是「人机交互」,车载是「人-车-环境」三方交互。

3.2.1 CarService架构解析

CarService是车载Android的核心服务,它管理着车辆的所有硬件抽象。我画了一张图,帮你理清它的层级关系。

车载系统服务架构图 车载应用层(Launcher / HVAC / 仪表) CarService(核心调度层) 车辆属性管理 / 策略路由 / 权限控制 Vehicle HAL(硬件抽象层) 车载硬件设备 CAN总线 / 以太网 / 串口 / GPIO 空调控制器 / 车窗 / 座椅 / 传感器 定制点 1. 新增车辆属性 2. 自定义Service 3. HAL层适配 数据流方向 应用 → CarService → Vehicle HAL → 硬件设备

3.2.2 自定义车载系统服务实战

假设我们需要一个「驾驶模式管理服务」,用来切换运动、舒适、经济三种模式。这个服务需要同时控制空调、悬架、油门响应等多个子系统。

第一步,定义AIDL接口:

// packages/services/Car/car-lib/src/android/car/IDrivingModeManager.aidl
package android.car;

interface IDrivingModeManager {
    int getCurrentMode();
    boolean setMode(int mode);
    void registerCallback(IDrivingModeCallback callback);
    void unregisterCallback(IDrivingModeCallback callback);
}

第二步,实现服务端逻辑:

// packages/services/Car/service/src/android/car/drivingmode/DrivingModeService.java
public class DrivingModeService extends IDrivingModeManager.Stub {
    private static final String TAG = "DrivingModeService";
    private int mCurrentMode = MODE_COMFORT;
    private final VehicleHal mVehicleHal;

    public DrivingModeService(VehicleHal hal) {
        mVehicleHal = hal;
    }

    @Override
    public boolean setMode(int mode) {
        // 检查权限
        CarServiceUtils.assertPermission(callingUid, Car.PERMISSION_CONTROL_CAR);
        
        // 通过Vehicle HAL下发到硬件
        mVehicleHal.setIntProperty(
            VehiclePropertyIds.DRIVING_MODE, 
            VehicleArea.GLOBAL, 
            mode
        );
        
        mCurrentMode = mode;
        notifyModeChanged(mode);
        return true;
    }
}

避坑指南:我曾经在自定义Service里直接操作硬件,结果发现权限检查没做,任何应用都能切换驾驶模式。记住:所有暴露给应用的接口,必须加 Car.PERMISSION_CONTROL_CAR 级别的权限校验。

3.3 系统属性配置:小配置,大影响

系统属性(System Properties)是Android里最容易被忽视但又极其重要的配置手段。在车载场景下,很多「奇怪的问题」最后都查到了属性配置上。

3.3.1 车载常用系统属性

属性名 默认值 车载推荐值 说明
ro.car.ui.headunit false true 启用车载UI模式
persist.car.rotation 0 0或90 屏幕旋转方向
ro.sf.lcd_density 320 160-240 DPI密度,影响UI缩放
persist.car.wakeup.gesture false true 支持手势唤醒
ro.car.thermal.limit 无 85000 温控阈值(毫摄氏度)

3.3.2 属性配置的最佳实践

属性配置有两种方式:编译时写死和运行时动态设置。我的建议是:

  • 编译时属性(ro.开头):用于硬件相关、不可变配置,比如屏幕参数、平台类型
  • 持久化属性(persist.开头):用于用户可调、需要重启生效的配置,比如亮度、音量
  • 运行时属性(无前缀):用于临时调试,重启后丢失

具体配置方法,在 device/generic/car/ 目录下的 system.prop 文件中添加:

# 车载系统属性配置示例
ro.car.ui.headunit=true
ro.sf.lcd_density=200
persist.car.rotation=0
persist.car.wakeup.gesture=true

# 性能相关
ro.sys.fw.bg_apps_limit=32
ro.vendor.perf.scroll_opt=true

# 音频策略
persist.car.audio.focus=true
ro.car.audio.samplerate=48000

血的教训:有一次我把 ro.sf.lcd_density 设成了120,结果所有车载应用都变得巨大无比,按钮都点不到。后来才发现,这个值必须和屏幕物理尺寸匹配,不能随便改。建议用公式:density = (屏幕宽度像素 / 物理宽度英寸) * 160 来计算。

3.4 编译产物验证与烧录

编译完成后,你会得到一堆镜像文件。车载系统通常需要烧录以下关键镜像:

# 编译产物路径
out/target/product/generic_car/

# 关键镜像文件
- system.img      # 系统分区
- vendor.img      # 厂商分区(BSP相关)
- boot.img        # 内核与ramdisk
- userdata.img    # 用户数据分区
- super.img       # 动态分区(Android 10+)

验证方法很简单:启动模拟器看看能不能跑起来。

# 启动车载模拟器
emulator -avd CarAVD -wipe-data -no-snapshot

# 或者直接烧录到真机
fastboot flashall -w

我的习惯:每次编译完,先跑一遍CTS(兼容性测试套件)中的车载相关用例,确保基础服务没问题。别等到集成测试才发现CarService起不来,那代价就大了。

好了,这一章的内容就到这里。AOSP下载编译、CarService定制、系统属性配置,这三块是车载Android系统定制的基石。你可能会觉得内容有点多,但相信我——把这些基础打牢了,后面做多屏交互、做HMI优化的时候,你会感谢今天自己的投入。