AAOS 到底是什么:车机上的 Android 和手机差在哪
AAOS 到底是什么:车机上的 Android 和手机差在哪
很多人第一次听说“车机跑的是 Android”,脑子里浮现的是一台放大版的平板:装了个横屏桌面,再塞几个导航和音乐 App。翻开源码会发现事情没这么简单,但也没有想象中那么“另起炉灶”。先看 system_server 里的一段代码:
// 节选:frameworks/base/services/java/com/android/server/SystemServer.java(android-14.0.0_r75)
boolean isAutomotive = mPackageManager
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
if (isAutomotive) {
t.traceBegin("StartCarServiceHelperService");
final SystemService cshs = mSystemServiceManager
.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
// 省略无关代码
}手机和车机跑的是同一份 SystemServer.java。区别从这个 if 开始:设备声明了 android.hardware.type.automotive 这个系统特性,system_server 就会多启动一个 CarServiceHelperService,后面一整套“车”的能力,都是从这里接上去的。
所以这篇文章想讲清楚的事情只有一件:AAOS 是 Android 本身,加上了一层“车”。这一层叫什么、放在哪、怎么用、行业里谁在用,下面一段一段拆开。
本文基准:AOSP
android-14.0.0_r75。源码路径都相对于 AOSP 根目录。本文边界:只建立整体认知,不展开 VHAL 的厂商实现、CAN 总线协议、EVS 摄像头链路和 Car App Library 的具体写法。
先把四个名字分开
车载 Android 的名字特别容易混,网上不少文章自己也写混了。先把最常见的四个分开:
| 名字 | 它是什么 | 跑在哪里 |
|---|---|---|
| Android Auto | 手机上的投屏平台,把手机里的界面投到兼容的车机屏幕上 | 手机 |
| Android Automotive OS(AAOS) | 直接装在车机硬件上的操作系统 | 车机 |
| Google Automotive Services(GAS) | Google 授权给车企的一组应用和服务,例如 Google 地图、Google Assistant、Google Play | 车机(AAOS 之上,需要授权) |
| Google built-in | 面向消费者的品牌名,指“这台车的 AAOS 带了 GAS” | 车机 |
这张表里最关键的一刀切在第一行和第二行之间。Android Auto 的计算发生在手机上,车机只是一块屏幕和一组按键,拔掉手机,Android Auto 就没了。AAOS 不需要手机,它就是车机本身的系统。AOSP 官方文档的说法也很直接:Android Auto 运行在用户的手机上,Android Automotive 运行在车载硬件上(见 What is Android Automotive?)。
第二个容易混的是 AAOS 和 GAS。AAOS 是 AOSP 的一部分,开源、免费,代码就在 packages/services/Car 等目录里,任何人都能下载编译。GAS 不在 AOSP 里,车企要单独找 Google 谈授权。你在 4S 店听到“这车是 Google 车机”,说的其实是“AAOS + GAS”,也就是 Google built-in。
这个区分对国内读者尤其重要。Google 的服务没有在中国大陆提供,国内销售的车型基本不会带 GAS。国内很多车机系统同样以 AOSP 为底座做深度定制,界面和应用生态由车企或供应商自己做,严格说它们用的是 AAOS 的开源部分,而不是 Google built-in。各家的定制深度不同,具体用了 AOSP 的哪些模块,通常只能看公开的技术资料或实际设备,这里不做逐家判断。
多出来的那一层放在哪
既然 AAOS 是 Android,那“车”的部分具体加在了哪里?AOSP 里 packages/services/Car/README.md 的第一行写着 Source code for Android Automotive OS,下面列了这个目录的结构:Car API 在 car-lib/,车载服务在 service/,车载产品配置在 car_product/。再加上 hardware/interfaces/automotive/ 下的车载 HAL 接口,就构成了 AAOS 相对手机多出来的那一层。

从上往下看这张图。
最上面是车载应用。AOSP 自带了一组车机专用的系统应用,在 packages/apps/Car/ 下面能看到 Launcher、SystemUI、Settings、Notification、Radio 等目录,产品配置 packages/services/Car/car_product/build/car.mk 把它们以 CarLauncher、CarSystemUI、CarSettings、CarRadioApp、CarMediaApp 等名字装进镜像。换句话说,车机的桌面、状态栏、设置都不是手机那一套,而是单独为车写的版本。
往下一层是 Car API,包名是 android.car,源码在 packages/services/Car/car-lib/。应用想读车速、调空调、查当前是不是在行驶,都通过这里。入口类是 android.car.Car,它提供 Car.createCar() 和 getCarManager(),再按需拿到 CarPropertyManager、CarAudioManager、CarUxRestrictionsManager 这类具体的管理器。
再往下是 CarService,它是 AAOS 的核心。这里有个很容易让人看错的地方:CarService 不在 system_server 里。system_server 里只有一个 CarServiceHelperService(frameworks/opt/car/services/builtInServices/src/com/android/internal/car/CarServiceHelperService.java),它的工作是用 bindService() 去绑定 action 为 android.car.ICar、包名为 com.android.car 的服务,并给 CarService 开一些只有 system_server 才有的权限通道。真正的 CarService 运行在独立的 com.android.car 进程里。看它的清单文件就清楚了:
<!-- 节选:packages/services/Car/service-builtin/AndroidManifest.xml(android-14.0.0_r75) -->
<manifest package="com.android.car"
coreApp="true"
android:sharedUserId="android.uid.system">
<application android:persistent="true">
<service android:name=".CarService"
android:singleUser="true"
android:exported="true">
<intent-filter>
<action android:name="android.car.ICar"/>
</intent-filter>
</service>
<!-- 省略无关代码 -->sharedUserId="android.uid.system" 让它以 system UID 运行,persistent="true" 让它常驻并在被杀后由系统重新拉起。com.android.car 只是一个壳,它会通过 UpdatablePackageContext 加载 com.android.car.updatable 包里的 CarServiceImpl,后者打包在 com.android.car.framework 这个 APEX 中(见 packages/services/Car/apex_car_framework/Android.bp)。这样设计的结果是,CarService 的主体逻辑可以随 APEX 单独更新,不必跟整个系统镜像一起升级。
CarService 内部又按功能拆成许多子服务,目录 packages/services/Car/service/src/com/android/car/ 下能看到 CarPropertyService、CarDrivingStateService、CarUxRestrictionsManagerService、audio/、power/、user/、cluster/、evs/ 等。后面讲“车特有的几件事”时会再回来看它们。
最底下是 VHAL,全称 Vehicle Hardware Abstraction Layer,接口定义在 hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl。它是 Android 世界和车辆世界的分界线。往上,它给 CarService 提供统一的“车辆属性”接口;往下,它由车企或供应商实现,去和 CAN、LIN、车载以太网上的各个 ECU(发动机、车身、空调等控制单元)打交道。AOSP 只给了接口和参考实现(hardware/interfaces/automotive/vehicle/aidl/impl/),其中 fake_impl/ 提供模拟数据,模拟器上看到的车速、挡位都来自这里;真车上对接车辆网络的那部分代码不在 AOSP 里。
跟着“车速”走一遍
光看模块图还是有点虚。拿一个最简单的需求走一遍:一个车载应用想显示当前车速。
应用侧的代码大致是这样(教程示例,省略了权限申请和生命周期处理):
// 教程示例:读取车速并监听变化
Car car = Car.createCar(context);
CarPropertyManager propertyManager = car.getCarManager(CarPropertyManager.class);
// 一次性读取,GLOBAL 区域的 areaId 是 0
float speed = propertyManager.getFloatProperty(
VehiclePropertyIds.PERF_VEHICLE_SPEED, 0);
// 持续监听,每秒更新 1 次
propertyManager.registerCallback(new CarPropertyManager.CarPropertyEventCallback() {
@Override
public void onChangeEvent(CarPropertyValue value) {
float metersPerSecond = (float) value.getValue();
// 更新界面
}
@Override
public void onErrorEvent(int propertyId, int areaId) {
// 属性读取失败
}
}, VehiclePropertyIds.PERF_VEHICLE_SPEED, 1f);几个点值得停下来看。
PERF_VEHICLE_SPEED 的单位是米每秒,不是公里每小时,VehicleProperty.aidl 的注释里写的是 @unit VehicleUnit.METER_PER_SEC。界面要显示 km/h,得自己乘 3.6。
读车速需要权限 android.car.permission.CAR_SPEED。它在 packages/services/Car/service/AndroidManifest.xml 里声明为 dangerous 级别,并归到位置权限组,和手机上的定位权限一样要在运行时向用户申请。车速能推出用户在哪、开得多快,这个归类很合理。
registerCallback() 的最后一个参数是更新频率(Hz)。车速是 CONTINUOUS 类型的属性,会持续变化,所以要告诉系统你想多久收到一次。挡位这类 ON_CHANGE 属性只在变化时上报,频率参数就不起作用了。
再看这个属性 ID 本身。VehiclePropertyIds.PERF_VEHICLE_SPEED 的值是 291504647,十六进制是 0x11600207。这个数不是随便编的,VehicleProperty.aidl 里它被拼成四段:
// 节选:hardware/interfaces/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
PERF_VEHICLE_SPEED = 0x0207 + 0x10000000 + 0x01000000
+ 0x00600000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:FLOAT0x10000000 表示这是 Android 定义的系统属性(厂商自定义属性用另一个分组),0x01000000 表示作用区域是整车(GLOBAL,相对的还有座椅、车门、车窗等区域),0x00600000 表示值类型是浮点数,最后的 0x0207 才是编号。看懂这个编码,以后在日志或 dumpsys 里看到一个十六进制属性 ID,就能先判断出它是系统属性还是厂商属性、按区域还是按整车、值是什么类型。
那这次调用从应用走到车上,经过了哪些地方?

第一跳是 Binder:应用进程里的 CarPropertyManager 调用 com.android.car 进程中的 CarPropertyService,后者就是 ICarProperty.Stub 的实现(packages/services/Car/service/src/com/android/car/CarPropertyService.java)。
第二跳在 CarService 内部:CarPropertyService 把请求交给 hal/PropertyHalService.java,再由 hal/VehicleHal.java 统一管理对 VHAL 的订阅和读写。
第三跳又是 Binder:AidlVehicleStub 按名字 android.hardware.automotive.vehicle.IVehicle/default 拿到 VHAL 服务,调用 IVehicle.getValues()(订阅走 subscribe())。参考实现的启动脚本里能看到它是一个独立的 vendor 进程:
# 节选:hardware/interfaces/automotive/vehicle/aidl/impl/vhal/ 下的 .rc 文件
service vendor.vehicle-hal-default /vendor/bin/hw/android.hardware.automotive.vehicle@V3-default-service
class early_hal
user vehicle_network最后一跳出了 Android:VHAL 实现从车辆网络拿到车速信号,再沿原路返回。这一段由车企实现,AOSP 管不到。
这条链路对排查很有用。车机上“车速不更新”的问题,可以按这三段分开查:应用有没有权限、有没有注册成功,是第一段;CarService 里有没有这个属性的配置,是第二段;VHAL 有没有上报数据,是第三段。调试命令后面会给。
车特有的几件事
读写车辆属性只是 AAOS 的一部分。手机上没有、车上却绕不开的问题还有不少,CarService 里基本都能找到对应的子服务。下面挑几个最能体现“车和手机不一样”的。
开车时不能随便看视频。 这是车机和手机在交互上最大的区别。CarDrivingStateService 根据车速、挡位判断当前是停车、怠速还是行驶,CarUxRestrictionsManagerService 再把驾驶状态翻译成具体的界面限制,比如不能输入长文本、列表要截断。应用的 Activity 如果没有声明 distractionOptimized(<meta-data android:name="distractionOptimized" android:value="true"/>),行驶时会被 CarPackageManagerService 挡住,换成一个阻断页面。这就是为什么 Google Play 上的车机应用要按类别审核:开发者文档里视频、游戏、浏览器这几类都标着“仅停车时可用”(见 Android for Cars 应用类别)。
系统用户不是司机。 手机上用户 0 就是机主。AAOS 产品配置里默认打开了 ro.fw.mu.headless_system_user=true(packages/services/Car/car_product/build/car.mk),也就是“无头系统用户”模式:用户 0 只在后台跑系统服务,不对应任何真人,司机和乘客是另外创建的用户。一辆车可能被一家人轮流开,每个人有自己的账号、座椅位置和常用导航,这个设计就是为此准备的。对开发者来说,最直接的影响是:调试时别默认“用户 0 就是我”。先用 adb shell am get-current-user 看清楚当前前台用户是谁(车机上通常不是 0),装应用、改设置、查数据目录时都按这个用户来。
声音要分区。 手机通常只有一组扬声器,车上有前排、后排、头枕,导航提示音要压低音乐而不是替代音乐。CarAudioService 会从 /vendor/etc/car_audio_configuration.xml 读取音频分区配置,把不同的声音用途路由到不同的输出设备上。
屏幕不止一块。 中控屏、仪表屏、副驾屏、后排屏,每块屏对应谁,由 CarOccupantZoneService 管理的“乘员区”决定。仪表屏相关的逻辑在 cluster/ClusterHomeService.java 等文件里。
熄火不等于关机。 车停了以后,系统可能还要下载更新、整理数据。CarService 的 garagemode/ 目录实现了“车库模式”:熄火后在限定时间内跑后台任务,再进入休眠。电源状态由 power/ 下的服务和 VHAL 上报的电源事件共同决定。
这五件事放在一起看,AAOS 相对手机的差异就清楚了:手机的 Framework 默认“一个人、一块屏、随时可以看”,车机的每一条假设都被改过。看懂这一点,读 CarService 源码时就知道每个子服务在补哪一条假设。
怎么上手
按目标不同,有三条路。
只想写一个车机应用。 用 Android Studio 的 Automotive 系统镜像建模拟器就够了,不用下载 AOSP。先看开发者文档的应用类别表:媒体、导航、充电、天气等类别有各自的实现方式,其中不少要求用 Android for Cars App Library 的模板来写界面,而不是随意布局,这是为了让应用在行驶中也能安全使用。自由度比手机低很多,这一点最好在立项时就想清楚。
想看 Framework 和 CarService 怎么跑。 自己编一个车机模拟器。device/generic/car/AndroidProducts.mk 里列出了可用的产品目标,android-14.0.0_r75 中包括:
source build/envsetup.sh
lunch sdk_car_x86_64-trunk_staging-userdebug
m
emulator源码下载和编译环境的准备与手机镜像相同,可以参考本站的 AOSP 环境与源码准备。
想在设备上直接看 CarService 的状态。 CarService 自带了 dumpsys 和 shell 命令,packages/services/Car/README.md 里有说明。下面的命令在 userdebug 车机模拟器上通过 adb shell 执行:
# 列出 CarService 里的所有子服务
dumpsys car_service --list
# 查看某个子服务的状态,例如属性服务
dumpsys car_service --services CarPropertyService
# 读取车速属性当前值(名字或十六进制 ID 都可以)
cmd car_service get-property-value PERF_VEHICLE_SPEED
# 查看属性配置:访问权限、变化模式、采样率范围
cmd car_service get-carpropertyconfig PERF_VEHICLE_SPEED
# 注入一个测试车速(单位米每秒),用来验证应用的回调
cmd car_service inject-vhal-event PERF_VEHICLE_SPEED 0 20get-property-value 读到值,说明 CarService 到 VHAL 这两段是通的,问题多半在应用侧;读不到或报不支持,就要回到 VHAL 的属性配置去查。这些命令只在 eng / userdebug 版本上能用。CarShellCommand.java 里有一张 USER_BUILD_COMMAND_TO_PERMISSION_MAP 白名单,user 版本上不在白名单里的命令会被直接拒绝,get-property-value 和 inject-vhal-event 都不在其中。量产车机一般是 user 版本,所以别指望在真车上用它们排查。完整命令列表用 cmd car_service -h 查看。
行业里谁在用,往哪走
聊完技术,再看看 AAOS 在行业里的位置。这一部分里,公开资料能确认的事实和我的判断会分开写。
带 GAS 的品牌越来越多。 Google 的 Google built-in 官方页面(2026 年 10 月查看)列出的兼容品牌包括 Volvo、Polestar、Renault、Alpine、Ford、Lincoln、Chevrolet、GMC、Buick、Cadillac、Honda、Acura、Nissan、Infiniti、Mitsubishi、Mazda 和 BYD。2020 年上市的 Polestar 2 是最早一批搭载 Google built-in 的量产车。这个页面现在的标题语是“With Google built‑in and Gemini”,说明 Google 正把它的大模型助手作为车机卖点推进。
最激进的一家是通用汽车。 2023 年 3 月,GM 宣布新款电动车将逐步取消 Apple CarPlay 和 Android Auto,完全依赖基于 AAOS 的车机(Google built-in),从 2024 款雪佛兰 Blazer EV 开始执行。2026 年 6 月,GM 又给 Google built-in 车型开放了 200 多个 Android Automotive 应用(9to5Google 报道)。但这件事还没有定论:2026 年 9 月 GM 公布的新款皮卡界面里出现了“更新的 Android Auto 和 Apple CarPlay”,电动车是否跟进,GM 没有确认(Electrek 报道)。手机投屏和车机原生之间的拉锯,至少在 GM 身上还在继续。
国内是另一条路。 由于 GAS 在国内不可用,国内车企基本不走 Google built-in 路线,而是在 AOSP 基础上自研或联合供应商定制车机系统,地图、语音、应用商店都换成本土生态。也有车企选择基于其他操作系统构建座舱。这一部分各家路线差异很大,公开资料里的“基于 Android”有时只是指兼容 Android 应用,并不一定意味着用了完整的 AAOS 车载框架,判断具体车型时要以技术资料为准。
AOSP 官方的方向是软件定义汽车。 AOSP 文档现在把 AAOS 的定位描述为“驱动车载信息娱乐系统,并逐步成为软件定义汽车(SDV)架构的基础”,还提到了在虚拟机上运行车辆服务、与仪表的安全显示结合等方向(同样见 What is Android Automotive?)。这些能力大多还在演进,不在本文基准的 Android 14 里。
下面是我的判断,供参考:
对想进入车载方向的 Android 工程师来说,AAOS 的门槛和机会都在“多出来的那一层”。应用层的差异主要是交互限制和模板化开发,学起来不难;真正稀缺的是能把 CarService、VHAL、音频分区、多用户和多屏这些车载特有模块和手机 Framework 的知识串起来的人。车机项目里常见的问题,比如开机到桌面慢、某个属性偶尔不上报、切换用户后某个应用状态错乱,往往横跨 system_server、com.android.car 和 VHAL 三个进程,单看哪一层都查不出来。
另外一个现实是,车的生命周期比手机长得多,一台车可能要维护很多年,而 AOSP 每年都在变。这也是 CarService 被做成可单独更新的 APEX 的原因之一。对车企来说,改动越贴近 Framework 内部,后续跟版本升级的成本就越高。我的建议是:能放在 VHAL 和 CarService 扩展点里做的定制,尽量不要直接改 frameworks/base。
回到开头
回到 SystemServer.java 里那个 if。AAOS 和手机 Android 是同一份代码,车机之所以是车机,是因为设备声明了 FEATURE_AUTOMOTIVE,于是 system_server 拉起 CarServiceHelperService,再由它拉起 com.android.car 进程里的 CarService;CarService 向上通过 android.car API 服务车载应用,向下通过 VHAL 连到车辆网络。车速、空调、驾驶限制、多用户、音频分区,都是在这条主干上长出来的。
想继续深入,可以从这三个入口开始:
- 在车机模拟器上运行
dumpsys car_service --list,看看 CarService 里到底有哪些子服务。 - 读
CarServiceHelperServiceUpdatableImpl.java里bindService()前后的代码,看system_server和 CarService 如何互相拿到对方的 Binder。 - 从
VehicleProperty.aidl里挑几个熟悉的属性,比如挡位GEAR_SELECTION、空调温度HVAC_TEMPERATURE_SET,用get-carpropertyconfig看它们的配置,体会属性 ID 的编码规则。
如果你还不熟悉 system_server 和 Binder,先读本站的 Android 系统如何从按下电源走到桌面 和 Binder 不等于 AIDL,再回来看 CarService 会顺很多。