04 · 车辆接口层与 IPC
本文讲清三件事:① 设置如何读写车辆硬件 ② 超时/错误如何处理 ③ AIDL 到底用在哪(对抗验证后的真相)。
一、核心结论(对抗验证后)
车辆硬件 IPC 完全走 Android Car 框架 API,不走自定义 AIDL。
MiCarSettings (app 进程)
│ android.car.* Binder IPC
▼
Car Service (系统进程)
│ IVehicle HIDL/AIDL
▼
Vehicle HAL → 车辆硬件
项目里没有用于车辆硬件的自定义 IVehicle/ICarSettingService AIDL。源码里出现的 IVehicle* 要么是进程内接口(IVehicleSyncHandler 账号同步用,非 AIDL),要么是注释里的 adb lshal debug 说明。
二、车辆数据流分层(自顶向下)
业务 Controller (如 WalkAwayLockController)
│ extends
▼
BaseVehicleNewSwitchPrefController (开关基类)
│ extends
▼
CarPropertyMgrPreferenceController<V> (中间层, implements CarPropertyManagerCallback)
│ 持有 mCarPropertyManager
▼
SettingsCarPropertyManager.kt (693 行, 每 Controller 一个, 非 singleton)
│ 绑定 Lifecycle 自动 release; 缓存 + 超时监控 + mock
│ 持有 mCarPropMgr
▼
VehicleControlManager (extends NewBaseCarServiceManager<CarPropertyManager>)
│ getServiceName() = Car.PROPERTY_SERVICE
▼
NewBaseCarServiceManager<T extends CarManagerBase> (Car 生命周期模板)
│ car.getCarManager(getServiceName())
▼
LocalCarManager (进程级 singleton)
│ Car.createCar(ctx, null, CAR_WAIT_TIMEOUT_WAIT_FOREVER, lifecycleListener)
│ 断线重连: MAX_RETRY=20, RETRY_INTERVAL=1500ms
▼
android.car.hardware.property.CarPropertyManager (framework, Binder → Car Service)
三、关键类职责
| 类 | 角色 |
|---|---|
LocalCarManager | 进程唯一 Car 实例持有者(双重检查锁单例),管理多订阅者链表 + 重连 |
NewBaseCarServiceManager<T> | 泛型模板:Car ready 时 getCarManager(name) 拿 T,died 时 release |
VehicleControlManager | Car.PROPERTY_SERVICE 包装;setIntProperty 走 ThreadPoolUtils.ASYNC 异步下发;提供 sync/async 两套 |
CarPropertyCacheManager | 进程级 singleton + callback 多路复用器:同 propId 多 callback 只向框架注册一次(SENSOR_RATE_ONCHANGE),本地 fan-out。⚠️ :192 行主线程同步注册是白屏元凶 |
SettingsCarPropertyManager | 业务门面(每 Controller 一个):缓存、超时、mock、Lifecycle 自动释放 |
GlobalCarPropertyManager | 进程级 singleton,SoftReference 持有 SettingsCarPropertyManager,供非页面逻辑用 |
CarPropertyRepo | 响应式 Flow 层:MutableStateFlow<CarPropertyValue>,UI 先行 |
CarPropertyManagerCallback | 每页回调契约(default 方法):onPropertyChanged / onSetPropertyTimeout / onSetPropertyError / onDataVerifyFailed / getTimeoutFlag |
CarPropertyTimeoutFlag | 枚举 CONTINUE / RESTART / REMOVE,控制超时计时器行为 |
VehiclePermanentListener | 进程级粘性监听器,Fragment 生命周期外保留最后值(用于可见性检查) |
VehiclePropertyConfig | 超时 + 缓存策略表(500/750/1000/2000/4000ms 多档,可 TIMEOUT_IGNORE) |
四、读流程
Controller.onCreateInternal
└─ SettingsCarPropertyManager.getIntProperty(propId, areaId)
├─ 查本地缓存 mPropertiesCache (key = propId#areaId)
├─ 命中 → 返回
└─ 未命中 → VehicleControlManager.getProperty
└─ framework CarPropertyManager.getProperty → Binder → HAL
└─ 隐式注册 callback(首次获取即订阅后续变化)
类型化包装:getIntProperty / getFloatProperty / getBooleanProperty / getIntArrayProperty / getStringProperty / getByteArrayProperty。动态属性(展车模式等)用 getIntPropertyWithoutCache 或 skipCache=true。
五、写流程(含超时监控)
[UI] 用户拨开关 MiCarNewSwitchPreference
▼
BaseVehicleNewSwitchPrefController.handlePreferenceChanged
├─ 判断 needSecondConfirm() → 可选二次确认对话框
└─ setCarProperty(isChecked)
└─ setVehicleProperty(obtainSetPropertyId(), getPropertyVal(isChecked), areaId)
└─ CarPropertyMgrPreferenceController.setVehicleProperty
└─ mCarPropertyManager.setCarProperty(propId, propVal, areaId, isSync)
├─ reportOneTrackWhenSetProperty(...) // 埋点
├─ mCarPropMgr.setIntProperty(propId, val, areaId)
│ └─ ThreadPoolUtils.ASYNC.execute(...) // 异步
│ └─ framework CarPropertyManager.setIntProperty → Binder → HAL
└─ startTimeOutMonitor(propId, val, areaId) // 启动超时计时
└─ mTimeoutHandler.sendMessageDelayed(...)
时长 = VehiclePropertyConfig.getCustomTimeoutValue(propId)
六、回流流程(信号变化 → UI 刷新)
Vehicle HAL → Car Service → framework CarPropertyManager.onChangeEvent
▼
CarPropertyCacheManager.mInternalCarPropertyEventCallback.onChangeEvent (:61)
├─ mPropertiesCache.put(key, value) // 全局缓存
└─ 遍历该 propId 的 callback set: callback.onChangeEvent(value)
└─ SettingsCarPropertyEventCallback.onChangeEvent (:662)
├─ cachePropVal(it) // 每页缓存
├─ processTimeout(it, getTimeoutFlag(it)) // REMOVE / RESTART / CONTINUE
└─ mCallback.onPropertyChanged(it)
└─ CarPropertyMgrPreferenceController.onPropertyChanged (:346)
├─ 过滤判断 (shouldIgnorePropertyFeedback / shouldUpdateUIByPropVal / 依赖信号映射)
└─ onHandlePropertyChange(value, shouldUpdateUI)
└─ preference.setChecked(isPropertyOpen(propVal)) // UI 更新
七、超时与错误回滚
- 每次
setCarProperty启动Handler延时任务(时长按 propId 查VehiclePropertyConfig)。 - 窗口期内收到回流 →
processTimeout按CarPropertyTimeoutFlag取消/重启/继续计时。 - 超时 →
onSetPropertyTimeout(propId, areaId)→restorePropertyToPrevious(读缓存旧值喂回onHandlePropertyChange,UI 回滚)。 - 错误 →
onErrorEvent→onSetPropertyError→ 同样回滚。 - 中间态信号(如充电口 OPENING/CLOSING)由
sValuesShouldBeCached缓存终态,避免 UI 抖动。 - 下行无回响时改听状态信号(
sTimeoutSignalMapping,如DRIVE_MODE_EXIT → STEERING_MODE)。
八、双订阅模式并存(架构演进)
| 模式 | 入口 | 特点 | 使用方 |
|---|---|---|---|
| 经典回调 | CarPropertyManagerCallback | 每 Controller 一个,绑 Preference 生命周期 | 大量存量页面(灯光/锁车/驾驶等) |
| Flow(新) | CarPropertyRepo.mapPropFlow / obtainPropertyFlow | 协程响应式,UI 先行(set 前 tryEmit,断线入队补发) | 新充电页(EnergyInfoViewModel/UseCase)、EnergyPreheatController |
两者底层都用相同的 registerCallback(...SENSOR_RATE_ONCHANGE)。
CarPropertyRepo.setCarProperty的「UI 先行」:先把值tryEmit到 StateFlow 让 UI 立即联动,再下发;若 Car Service 未连则入mSetPropertyQueue,连上后补发。
九、AIDL 真相(对抗验证后澄清)
项目共 8 个 AIDL,全部不用于车辆硬件:
| AIDL | 模块 | 用途 |
|---|---|---|
IAccountConfirmResponse.aidl | settingsBaseLib | 小米账号确认回调 |
ILicenseMasterInterface.aidl | settingsBaseLib | 许可证/法律文本分发 |
LicenseTextSingle.aidl / LicenseInfoDistributeSingle.aidl | settingsBaseLib | 许可证 parcelable |
IBALService.aidl + IBookChargingMsgListener.aidl + BookChargingMsg.aidl | micarChargeSettings | 预约充电位置服务(与 com.micar.scene 场景应用通信) |
IStorageCleaner.aidl | settingsSystem | 存储清理(getClearableSize/clearStorage) |
base/settingsVehicleLib 内确实没有任何 AIDL(base agent 的结论限定在该模块内正确)。车辆硬件只走
Car.PROPERTY_SERVICE。
唯一的跨应用车辆相关 IPC:BAL(预约充电位置)
micarChargeSettings/BALServiceSDK
└─ bindService(Intent(pkg=com.micar.scene, action=com.micar.scene.BAL_SERVICE))
└─ IBALService.Stub.asInterface(binder)
├─ linkToDeath + 指数重连(max 256s)
├─ registerBookChargingMsgListener(listener) // AIDL 调用
└─ getBookChargingMsg() // AIDL 调用
设置 ↔ 场景应用(com.micar.scene)之间传 POI/位置数据,独立 IPC 通道。
十、其他 Car Service 使用
| Service | Manager | 用途 |
|---|---|---|
Car.PROPERTY_SERVICE | VehicleControlManager | 主车辆属性(绝大多数车控) |
Car.AUDIO_SERVICE | CarAudioControlManager → GlobalCarAudioControlManager | 音量/音频(音量页用,不走 CarProperty) |
Car.PROPERTY_SERVICE | CarConfigManager | CCP 配置字(出厂烧录,车辆生命周期不变,含动力类型/硬件开关) |
CCP 配置字要点:getIntProperty(propId, default, skipCache) 用 Int.MIN_VALUE 作哨兵探测 CarService 可用性(只有非哨兵值才写缓存,避免服务不可用时污染);OTA/重启后调 clearCache()。
十一、各车辆域 → UI 模块映射
车辆信号 ID 定义在 base/settingsBaseLib/.../miauto/vehicle/MiCarPropertyIds.java(2126 行,按域分内部类):
| 域 | MiCarPropertyIds 内部类 | UI 模块 |
|---|---|---|
| 灯光 | LightCtrl | micarLightSettings |
| 充电/能量 | EnergyManagement | micarChargeSettings |
| 锁/门/窗 | LockCtrl, DoorCtrl, WindowCtrl | micarLockSettings |
| 驾驶/底盘 | DrivingCtrl | micarDrivingSettings |
| 车身/座椅/后视镜 | BodyCtrl, SeatAdjustControl, MirrorControl, Steering | VehicleBodyControl |
| 智驾 | AutoPilot | settingsAutoPilot |
| 安全 | SafeAndSecurity, WarningAssist, SafeModeCtrl | micarSafetyServiceSettings |
| 显示/HUD | Display, Hud | micarDisplaySettings |
| 数字钥匙 | DigitalKey | micarConnectionSettings |
| 音频 | —(走 CarAudioControlManager) | micarVolume |
| IoT | —(走 IotClientManager,非 CarProperty) | micarIotSettings |