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 (androidx.car.app) 应用侧依赖 | 提供模板化UI (Template) | 生命周期托管 | 导航/停车/充电等场景化API 代表组件: CarAppService, Screen, Template, CarNavigationManager Binder/AIDL 调用 Car API (android.car.*) 系统级接口封装 | 暴露给普通应用与系统应用 | 基于@SystemApi或@CarApi限制访问 代表接口: Car, CarInfoManager, CarPropertyManager, CarPackageManager, CarUxRestrictionManager 系统服务注册与获取 CarService (System Server 内运行) 核心服务容器 | 管理车辆属性 | 策略仲裁 | 与VHAL交互 内部模块: CarPropertyService, CarUxRestrictionService, CarAudioService, CarNavigationService, CarPackageService 启动时机: SystemServer 启动阶段 | 权限: 仅系统签名或特定特权应用可绑定 属性读写/事件订阅 Vehicle HAL (VHAL) 硬件抽象层 | 定义车辆属性(Property)与信号 | 由OEM实现 | 通过HIDL/AIDL与CarService通信 普通Android 无CarService 无VHAL 无驾驶状态感知 应用自由后台运行 无UxRestriction 无CarAppLibrary 窗口管理基于Activity 无车辆属性订阅 无驾驶模式切换 无EV/充电管理 无CarAudio焦点 无CarNavigation 无CarProperty 无CarPackage管理 无CarUxRestriction 无CarAppFocus 无CarOccupantZone 无CarGarageMode 应用层 框架层 系统服务层 HAL层

上图清晰地展示了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中完全不存在,它要求应用必须感知并响应驾驶状态的变化。

对比维度普通AndroidAndroid 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:

  1. 检查系统服务列表:在adb shell中执行service list | grep car,如果输出包含car_service,则说明系统集成了CarService。
  2. 检查系统属性:执行getprop | grep ro.android.automotive,AAOS通常会设置ro.android.automotive=true。
  3. 检查包管理器:查看是否存在com.android.car包(CarService的宿主APK),执行pm list packages | grep car。
  4. 检查VHAL实现:执行lshal | grep vehicle,AAOS必须存在Vehicle HAL的实现(如android.hardware.automotive.vehicle@2.0::IVehicle)。
  5. 检查Car App Library支持:在应用层,如果系统预置了Google Automotive Services(GAS)或支持androidx.car.app的模板化应用,则基本可确认是AAOS。
经验提醒:部分车机厂商基于AOSP进行深度定制,可能移除了CarService而改用自研方案,此时系统虽然运行在车上,但并非标准AAOS。在定制开发前,务必确认系统是否保留了完整的CarService框架,否则后续的Car API调用将无法工作。

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 常见误区与总结

风险提示:不要试图通过修改系统属性或禁用CarUxRestrictionService来绕过驾驶限制。这不仅是安全违规行为,还可能导致车规认证失败,甚至引发法律风险。AAOS的设计初衷是保障驾驶员和乘客的安全,任何定制都应在安全框架内进行。

总结而言,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应用

  1. 添加依赖:在build.gradle中添加implementation 'androidx.car.app:app:1.4.0'(版本号以最新为准)。
  2. 声明权限:在AndroidManifest.xml中声明<uses-permission android:name="android.car.permission.CAR_APP_LIBRARY" />。
  3. 定义CarAppService:创建一个继承自CarAppService的类,并实现onCreateScreen方法。
  4. 实现Screen与Template:在onCreateScreen中返回一个Screen实例,并在其onGetTemplate方法中返回具体的Template。
  5. 处理生命周期:重写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 常见误区与总结

重点提示:Car App Library的应用(即通过CarAppService实现的应用)与普通Android应用(使用Activity)在AAOS上可以共存。但前者更适合驾驶场景,后者更适合停车时使用的复杂应用。系统通过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子服务主要功能典型使用场景
CarPropertyManagerCarPropertyService读写车辆属性(车速、电量、空调温度等)显示实时车速、调节空调
CarUxRestrictionManagerCarUxRestrictionService获取/监听驾驶限制状态根据驾驶状态切换应用模式
CarAudioManagerCarAudioService管理音频焦点、音量分组、音区媒体播放、导航语音混音
CarNavigationManagerCarNavigationService导航状态上报、转向提示导航应用与仪表盘联动
CarPackageManagerCarPackageService管理车载应用(启用/禁用、白名单)OEM预装应用管理
CarInfoManagerCarInfoService获取车辆基本信息(VIN、型号)车辆身份识别
CarOccupantZoneManagerCarOccupantZoneService管理乘客区域(Driver/Passenger)多区域音视频隔离
CarGarageModeManagerCarGarageModeService车库模式(停车时维护)OTA升级、系统维护

3.3.2 判断方法:如何定位Car API与CarService的对应关系

在定制开发中,经常需要定位某个功能是由哪个Manager提供的,以及其底层实现位于CarService的哪个模块。可以通过以下步骤进行:

  1. 查看API文档:在AOSP源码中,packages/services/Car/car-lib/src/android/car/目录下定义了所有Car API接口。
  2. 查找AIDL文件:每个Manager对应一个AIDL接口,位于packages/services/Car/car-lib/src/android/car/aidl/目录下。例如ICarProperty.aidl对应CarPropertyManager。
  3. 追踪服务注册:在CarService.java的init()方法中,可以看到所有子服务的注册代码,例如mPropertyService = new CarPropertyService(...)。
  4. 使用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没有将两者关联的逻辑。
  • 操作步骤:
    1. 在CarService中新增一个SpeedVolumeCompensationService,监听车速变化。
    2. 在CarPropertyService中注册对车速属性的订阅,当车速变化时回调到新服务。
    3. 在新服务中计算目标音量:targetVolume = baseVolume + speed * coefficient,并通过CarAudioService的setGroupVolume方法设置音量。
    4. 增加防抖逻辑,避免音量频繁跳动。
  • 结果对比:定制后,车速从60km/h提升到120km/h时,媒体音量自动增加约15%,且导航语音音量保持不变(因为导航使用不同的音频分组)。
  • 风险与代价:该功能涉及CarService的修改,需要重新编译系统镜像,且必须通过车规级测试(如音量变化不能引起听觉不适)。
  • 复盘经验:CarService的定制必须遵循模块化原则,新增功能应作为独立的子服务,避免修改现有服务的核心逻辑。

3.3.4 常见误区与总结

风险提示:Car API中标注为@SystemApi的接口,普通第三方应用无法调用,即使声明了权限也会被拒绝。在定制时,如果OEM希望开放某些接口给第三方,需要修改权限定义或提供签名权限。切勿直接移除权限检查,否则会引入安全漏洞。

总结:Car API与CarService的层次关系是AAOS架构的基石。应用通过Car API获取车辆数据和控制车辆功能,CarService则负责具体的业务逻辑和与VHAL的交互。理解这一层次关系,是进行CarService定制的前提。

3.4 三者协作的完整链路与实战要点

3.4.1 核心原理:一次完整的车辆数据读取流程

以“读取当前车速并显示在应用界面上”为例,完整链路如下:

  1. VHAL层:车辆CAN总线上的车速信号通过VHAL驱动采集,并转换为VehicleProperty值(PERFORMANCE_VEHICLE_SPEED),通过HIDL/AIDL接口上报给CarService。
  2. CarService层:CarPropertyService接收到属性变化,更新内部缓存,并通知所有订阅了该属性的客户端。
  3. Car API层:应用通过CarPropertyManager.registerCallback()注册监听,当属性变化时,回调onPropertyChanged方法。
  4. 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回调,但系统设置界面可以正常显示车速。
  • 分析过程:
    1. 首先确认应用是否声明了android.car.permission.CAR_SPEED权限(或CAR_PROPERTY权限)。
    2. 检查应用是否通过Car.createCar()成功获取Car对象,并正确获取CarPropertyManager。
    3. 检查应用是否注册了正确的属性ID(PERFORMANCE_VEHICLE_SPEED)和回调。
    4. 使用dumpsys car_service查看是否有客户端注册了该属性的订阅。
  • 关键发现:应用在AndroidManifest中声明了权限,但未在运行时通过CarPropertyManager.registerCallback()注册,而是错误地使用了getProperty()轮询方式,且未处理回调。
  • 解决方案:修改代码,使用registerCallback()方式订阅属性变化,并在onPropertyChanged中更新UI。
  • 结果对比:修改后,应用能够实时收到车速更新,超速提醒功能恢复正常。
  • 复盘经验:CarPropertyManager支持两种模式:轮询(getProperty)和订阅(registerCallback ← 上一章 📖 返回目录 下一章 →