3、Android Automotive基础:理解Android Automotive OS与普通Android的区别,掌握Car App Library、Car API、CarService的层次关系
Android Automotive OS(AAOS)并非简单地在普通Android上叠加几个汽车应用,而是一次针对车载环境的系统性重构。从内核驱动到应用框架,从权限模型到生命周期管理,AAOS在继承AOSP(Android Open Source Project)核心的同时,引入了大量汽车专属的抽象层与服务。本章将聚焦于AAOS与普通Android的本质差异,并深入剖析Car App Library、Car API与CarService三者之间的协作关系,为后续的CarService定制打下坚实的认知基础。
上图清晰地展示了AAOS中从应用到硬件的完整调用链。Car App Library位于应用层,为开发者提供高层次的模板化UI与场景化API;Car API作为系统接口层,将CarService的能力以Java接口形式暴露给应用;CarService则运行在System Server进程中,是车辆功能的实际管理者,最终通过VHAL与车辆硬件通信。理解这条链路是进行任何CarService定制的前提。
3.1 AAOS与普通Android的核心差异
3.1.1 核心原理:从移动设备到车载环境的范式转移
普通Android的设计目标是通用移动计算,其假设是设备由单个用户持有,屏幕尺寸在5-7英寸之间,交互以触摸为主,且应用可以自由地在后台运行、推送通知、访问网络。而AAOS的设计目标发生了根本性变化:安全优先、驾驶场景适配、多用户/多区域支持、硬件资源受限。这些差异并非简单的功能增删,而是从系统架构层面重新定义了应用的生命周期、权限模型和UI范式。
最核心的差异体现在驾驶安全约束(Driver Distraction)上。AAOS引入了CarUxRestrictionManager,系统可以根据车辆行驶状态(Parked、Idling、Moving)动态限制应用的可用性。例如,当车速超过某个阈值时,视频播放应用会被强制暂停,键盘输入会被禁用,列表项的最小点击区域会被放大。这种机制在普通Android中完全不存在,它要求应用必须感知并响应驾驶状态的变化。
| 对比维度 | 普通Android | Android Automotive OS |
|---|---|---|
| 目标设备 | 手机、平板 | 车载信息娱乐系统(IVI)、仪表盘 |
| 交互方式 | 触摸为主,支持键盘鼠标 | 触摸+旋钮+方向盘按键+语音 |
| 应用生命周期 | 自由后台运行,进程被杀可恢复 | 受驾驶状态约束,后台受限,需遵循Car App Library生命周期 |
| 权限模型 | 运行时权限,用户可授予/撤销 | 系统权限+Car权限(@SystemApi),部分权限仅OEM系统应用可获取 |
| UI范式 | 自由布局,多窗口 | 模板化UI(Template),强制安全间距,禁止复杂手势 |
| 硬件访问 | Camera、GPS、Sensor等通用硬件 | 额外暴露车辆信号(车速、档位、转向灯、电量、胎压等) |
| 多用户支持 | 支持多用户,但切换不频繁 | 支持多用户+多区域(Driver/Passenger),可同时运行不同用户会话 |
| 系统服务 | ActivityManager、WindowManager等 | 额外包含CarService及其子服务(CarProperty、CarAudio、CarNavigation等) |
| 启动流程 | Bootloader -> Kernel -> Init -> Zygote -> SystemServer | 在SystemServer启动后额外启动CarService,并等待VHAL就绪 |
3.1.2 判断方法:如何识别一个系统是否为AAOS
在实际开发或系统评估中,可以通过以下方法快速判断一个Android系统是否为AAOS:
- 检查系统服务列表:在adb shell中执行
service list | grep car,如果输出包含car_service,则说明系统集成了CarService。 - 检查系统属性:执行
getprop | grep ro.android.automotive,AAOS通常会设置ro.android.automotive=true。 - 检查包管理器:查看是否存在
com.android.car包(CarService的宿主APK),执行pm list packages | grep car。 - 检查VHAL实现:执行
lshal | grep vehicle,AAOS必须存在Vehicle HAL的实现(如android.hardware.automotive.vehicle@2.0::IVehicle)。 - 检查Car App Library支持:在应用层,如果系统预置了Google Automotive Services(GAS)或支持
androidx.car.app的模板化应用,则基本可确认是AAOS。
3.1.3 案例拆解:一个视频应用在AAOS上的行为差异
假设我们开发了一个视频播放应用,在普通Android手机上,用户可以自由播放视频,应用可以后台播放音频,甚至通过画中画模式悬浮播放。但在AAOS上,该应用的行为将发生显著变化:
- 初始现象:应用在车机上安装后,启动正常,但一旦车辆开始行驶(车速>5km/h),视频画面立即被系统强制暂停,并显示“驾驶时不可观看”的提示。
- 分析过程:通过查看logcat,发现系统发送了
CarUxRestrictionManager.UX_RESTRICTION_MODE_NO_VIDEO的广播。这是因为AAOS的CarUxRestrictionService根据VHAL上报的车速信号,动态调整了UX限制级别。 - 解决方案:应用需要实现
CarUxRestrictionManager.OnUxRestrictionsChangedListener接口,在收到限制变化时,主动暂停视频播放并切换到音频模式或显示安全提示界面。同时,应用应使用Car App Library的Template来构建UI,因为模板会自动适配不同驾驶状态下的布局约束。 - 结果对比:未适配的应用在行驶中会被系统强制黑屏或退出,用户体验极差;适配后的应用则能优雅地切换模式,保持可用性。
- 复盘经验:任何面向AAOS的应用开发,都必须将驾驶状态作为第一优先级考虑因素,而不是仅仅关注功能实现。
3.1.4 常见误区与总结
总结而言,AAOS与普通Android的差异是全方位的,从系统服务到应用框架,从权限模型到UI规范,都围绕“车载安全”这一核心目标进行了重构。开发者必须摒弃移动端的开发习惯,转而遵循车载场景的约束与规范。
3.2 Car App Library:应用开发的加速器
3.2.1 核心原理:模板化UI与场景化生命周期
Car App Library(androidx.car.app)是Google为AAOS应用开发提供的一套Jetpack库。它的核心价值在于将复杂的车载UI适配和驾驶安全约束封装为可复用的模板组件。开发者无需关心不同车机屏幕的尺寸差异、旋钮焦点管理、驾驶状态切换等底层细节,只需专注于业务逻辑。
该库的核心组件包括:
- CarAppService:应用的入口Service,类似于Activity,但生命周期由系统管理。应用必须继承该类并实现
onCreateScreen方法。 - Screen:代表一个UI界面,类似于Fragment。每个Screen对应一个Template。
- Template:预定义的UI模板,如
NavigationTemplate(导航)、PlaceListTemplate(地点列表)、MessageTemplate(消息)、SearchTemplate(搜索)等。模板强制了安全间距、最小点击区域和焦点规则。 - CarNavigationManager:用于导航应用与系统导航栏交互,例如更新导航状态、提供转向提示等。
- CarAppFocusManager:管理应用焦点,例如导航应用在导航时获得焦点,媒体应用在播放时获得焦点。
3.2.2 操作流程:创建一个Car App Library应用
- 添加依赖:在
build.gradle中添加implementation 'androidx.car.app:app:1.4.0'(版本号以最新为准)。 - 声明权限:在
AndroidManifest.xml中声明<uses-permission android:name="android.car.permission.CAR_APP_LIBRARY" />。 - 定义CarAppService:创建一个继承自
CarAppService的类,并实现onCreateScreen方法。 - 实现Screen与Template:在
onCreateScreen中返回一个Screen实例,并在其onGetTemplate方法中返回具体的Template。 - 处理生命周期:重写
onAppFocusChanged、onCarConfigurationChanged等方法,响应系统事件。
public class MyCarAppService extends CarAppService {
@Override
public Screen onCreateScreen(Intent intent) {
return new MyScreen(getCarContext());
}
private static class MyScreen extends Screen {
MyScreen(CarContext carContext) { super(carContext); }
@Override
public Template onGetTemplate() {
// 返回一个简单的消息模板
MessageTemplate.Builder builder = new MessageTemplate.Builder("欢迎使用车载应用");
builder.setTitle("示例应用");
return builder.build();
}
}
}
3.2.3 案例拆解:导航应用的模板化实现
以导航应用为例,传统开发方式需要自行处理地图渲染、手势缩放、道路信息叠加等复杂功能,且必须适配不同车机的分辨率。使用Car App Library后,开发流程被大幅简化:
- 背景与约束:导航应用需要显示地图、当前路线、转向提示,并支持驾驶员通过旋钮或语音操作。系统要求导航应用必须使用
NavigationTemplate,且地图区域不能覆盖超过屏幕的2/3。 - 实现方式:应用通过
NavigationTemplate.Builder设置导航状态(路线、剩余距离、预计到达时间),并通过CarNavigationManager向系统注册导航会话。地图渲染由应用自研或使用第三方SDK,但必须嵌入到Template的SurfaceCallback中。 - 关键参数:
NavigationTemplate要求提供NavigationInfo(导航信息)、Step(转向步骤)、Destination(目的地)等数据。系统会根据驾驶状态自动调整模板的显示方式,例如在行驶中隐藏详细列表,仅显示简洁的转向提示。 - 结果对比:使用模板后,应用在不同车机上的UI表现高度一致,且自动满足安全规范,无需针对每款车型单独适配。
- 复盘经验:Car App Library并非万能,对于需要高度定制化UI的应用(如游戏、复杂设置页),模板可能不够灵活。此时可以考虑使用普通Activity,但必须自行处理驾驶限制。
3.2.4 常见误区与总结
CarPackageManager来区分两类应用,并给予不同的权限和限制。
总结:Car App Library是AAOS应用开发的首选方式,它通过模板化UI和托管生命周期,大幅降低了车载应用的开发复杂度,同时确保了安全合规性。
3.3 Car API与CarService的层次关系
3.3.1 核心原理:接口与实现的分离
Car API(android.car.*)是系统提供给应用层的Java接口集合,而CarService是这些接口的底层实现。这种设计遵循了Android系统一贯的接口与实现分离原则:应用只依赖Car API,不直接与CarService通信,从而保证了API的稳定性和系统内部的可演化性。
CarService运行在独立的系统进程中(com.android.car),通过AIDL(Android Interface Definition Language)暴露Binder接口。应用通过Car.createCar(context)获取Car对象,再通过car.getCarManager(CarPropertyManager.class)等方式获取具体的Manager。每个Manager对应CarService中的一个子服务。
| Car API 接口 | 对应CarService子服务 | 主要功能 | 典型使用场景 |
|---|---|---|---|
| CarPropertyManager | CarPropertyService | 读写车辆属性(车速、电量、空调温度等) | 显示实时车速、调节空调 |
| CarUxRestrictionManager | CarUxRestrictionService | 获取/监听驾驶限制状态 | 根据驾驶状态切换应用模式 |
| CarAudioManager | CarAudioService | 管理音频焦点、音量分组、音区 | 媒体播放、导航语音混音 |
| CarNavigationManager | CarNavigationService | 导航状态上报、转向提示 | 导航应用与仪表盘联动 |
| CarPackageManager | CarPackageService | 管理车载应用(启用/禁用、白名单) | OEM预装应用管理 |
| CarInfoManager | CarInfoService | 获取车辆基本信息(VIN、型号) | 车辆身份识别 |
| CarOccupantZoneManager | CarOccupantZoneService | 管理乘客区域(Driver/Passenger) | 多区域音视频隔离 |
| CarGarageModeManager | CarGarageModeService | 车库模式(停车时维护) | OTA升级、系统维护 |
3.3.2 判断方法:如何定位Car API与CarService的对应关系
在定制开发中,经常需要定位某个功能是由哪个Manager提供的,以及其底层实现位于CarService的哪个模块。可以通过以下步骤进行:
- 查看API文档:在AOSP源码中,
packages/services/Car/car-lib/src/android/car/目录下定义了所有Car API接口。 - 查找AIDL文件:每个Manager对应一个AIDL接口,位于
packages/services/Car/car-lib/src/android/car/aidl/目录下。例如ICarProperty.aidl对应CarPropertyManager。 - 追踪服务注册:在
CarService.java的init()方法中,可以看到所有子服务的注册代码,例如mPropertyService = new CarPropertyService(...)。 - 使用dumpsys:执行
adb shell dumpsys car_service可以查看CarService内部各子服务的状态信息,帮助定位问题。
3.3.3 案例拆解:定制一个“根据车速自动调节音量”的功能
该功能需要同时使用CarPropertyManager(读取车速)和CarAudioManager(调节音量),并涉及CarService的定制。
- 背景与约束:某OEM希望实现车速自适应音量补偿(Speed-sensitive Volume Compensation),即车速越高,媒体音量自动增大,以抵消风噪和胎噪。要求响应延迟小于200ms,且不能影响导航语音的清晰度。
- 初始现象:在AOSP默认实现中,音量是固定的,不随车速变化。
- 分析过程:首先确认CarPropertyManager可以读取到车速属性(
VehiclePropertyIds.PERFORMANCE_VEHICLE_SPEED),且CarAudioManager可以调整媒体音量的分组(CarAudioManager.AUDIO_USAGE_MEDIA)。但默认的CarService没有将两者关联的逻辑。 - 操作步骤:
- 在CarService中新增一个
SpeedVolumeCompensationService,监听车速变化。 - 在
CarPropertyService中注册对车速属性的订阅,当车速变化时回调到新服务。 - 在新服务中计算目标音量:
targetVolume = baseVolume + speed * coefficient,并通过CarAudioService的setGroupVolume方法设置音量。 - 增加防抖逻辑,避免音量频繁跳动。
- 在CarService中新增一个
- 结果对比:定制后,车速从60km/h提升到120km/h时,媒体音量自动增加约15%,且导航语音音量保持不变(因为导航使用不同的音频分组)。
- 风险与代价:该功能涉及CarService的修改,需要重新编译系统镜像,且必须通过车规级测试(如音量变化不能引起听觉不适)。
- 复盘经验:CarService的定制必须遵循模块化原则,新增功能应作为独立的子服务,避免修改现有服务的核心逻辑。
3.3.4 常见误区与总结
@SystemApi的接口,普通第三方应用无法调用,即使声明了权限也会被拒绝。在定制时,如果OEM希望开放某些接口给第三方,需要修改权限定义或提供签名权限。切勿直接移除权限检查,否则会引入安全漏洞。
总结:Car API与CarService的层次关系是AAOS架构的基石。应用通过Car API获取车辆数据和控制车辆功能,CarService则负责具体的业务逻辑和与VHAL的交互。理解这一层次关系,是进行CarService定制的前提。
3.4 三者协作的完整链路与实战要点
3.4.1 核心原理:一次完整的车辆数据读取流程
以“读取当前车速并显示在应用界面上”为例,完整链路如下:
- VHAL层:车辆CAN总线上的车速信号通过VHAL驱动采集,并转换为VehicleProperty值(
PERFORMANCE_VEHICLE_SPEED),通过HIDL/AIDL接口上报给CarService。 - CarService层:
CarPropertyService接收到属性变化,更新内部缓存,并通知所有订阅了该属性的客户端。 - Car API层:应用通过
CarPropertyManager.registerCallback()注册监听,当属性变化时,回调onPropertyChanged方法。 - Car App Library层:如果应用使用Car App Library,可以通过
CarContext.getCarManager()获取CarPropertyManager,并在Screen中更新UI。
3.4.2 操作流程:调试CarService与VHAL的通信
# 1. 查看VHAL是否正常启动
adb shell lshal | grep vehicle
# 2. 查看CarService是否注册成功
adb shell service list | grep car
# 3. 查看车辆属性值(以车速为例)
adb shell dumpsys car_service | grep -A 5 "PERFORMANCE_VEHICLE_SPEED"
# 4. 模拟车速变化(需要root权限)
adb shell cmd car_service set-property PERFORMANCE_VEHICLE_SPEED 50
# 5. 观察应用日志
adb logcat -s CarPropertyService:V CarAppLibrary:V
3.4.3 案例拆解:诊断“应用收不到车速更新”的问题
- 背景:某导航应用在车机上无法获取实时车速,导致超速提醒功能失效。
- 初始现象:应用日志中无任何
onPropertyChanged回调,但系统设置界面可以正常显示车速。 - 分析过程:
- 首先确认应用是否声明了
android.car.permission.CAR_SPEED权限(或CAR_PROPERTY权限)。 - 检查应用是否通过
Car.createCar()成功获取Car对象,并正确获取CarPropertyManager。 - 检查应用是否注册了正确的属性ID(
PERFORMANCE_VEHICLE_SPEED)和回调。 - 使用
dumpsys car_service查看是否有客户端注册了该属性的订阅。
- 首先确认应用是否声明了
- 关键发现:应用在AndroidManifest中声明了权限,但未在运行时通过
CarPropertyManager.registerCallback()注册,而是错误地使用了getProperty()轮询方式,且未处理回调。 - 解决方案:修改代码,使用
registerCallback()方式订阅属性变化,并在onPropertyChanged中更新UI。 - 结果对比:修改后,应用能够实时收到车速更新,超速提醒功能恢复正常。
- 复盘经验:CarPropertyManager支持两种模式:轮询(
getProperty)和订阅(registerCallback