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
VehicleControlManagerCar.PROPERTY_SERVICE 包装;setIntPropertyThreadPoolUtils.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。动态属性(展车模式等)用 getIntPropertyWithoutCacheskipCache=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)。
  • 窗口期内收到回流 → processTimeoutCarPropertyTimeoutFlag 取消/重启/继续计时。
  • 超时onSetPropertyTimeout(propId, areaId)restorePropertyToPrevious(读缓存旧值喂回 onHandlePropertyChangeUI 回滚)。
  • 错误onErrorEventonSetPropertyError → 同样回滚。
  • 中间态信号(如充电口 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.aidlsettingsBaseLib小米账号确认回调
ILicenseMasterInterface.aidlsettingsBaseLib许可证/法律文本分发
LicenseTextSingle.aidl / LicenseInfoDistributeSingle.aidlsettingsBaseLib许可证 parcelable
IBALService.aidl + IBookChargingMsgListener.aidl + BookChargingMsg.aidlmicarChargeSettings预约充电位置服务(与 com.micar.scene 场景应用通信)
IStorageCleaner.aidlsettingsSystem存储清理(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 使用

ServiceManager用途
Car.PROPERTY_SERVICEVehicleControlManager主车辆属性(绝大多数车控)
Car.AUDIO_SERVICECarAudioControlManagerGlobalCarAudioControlManager音量/音频(音量页用,不走 CarProperty)
Car.PROPERTY_SERVICECarConfigManagerCCP 配置字(出厂烧录,车辆生命周期不变,含动力类型/硬件开关)

CCP 配置字要点getIntProperty(propId, default, skipCache)Int.MIN_VALUE 作哨兵探测 CarService 可用性(只有非哨兵值才写缓存,避免服务不可用时污染);OTA/重启后调 clearCache()

十一、各车辆域 → UI 模块映射

车辆信号 ID 定义在 base/settingsBaseLib/.../miauto/vehicle/MiCarPropertyIds.java(2126 行,按域分内部类):

MiCarPropertyIds 内部类UI 模块
灯光LightCtrlmicarLightSettings
充电/能量EnergyManagementmicarChargeSettings
锁/门/窗LockCtrl, DoorCtrl, WindowCtrlmicarLockSettings
驾驶/底盘DrivingCtrlmicarDrivingSettings
车身/座椅/后视镜BodyCtrl, SeatAdjustControl, MirrorControl, SteeringVehicleBodyControl
智驾AutoPilotsettingsAutoPilot
安全SafeAndSecurity, WarningAssist, SafeModeCtrlmicarSafetyServiceSettings
显示/HUDDisplay, HudmicarDisplaySettings
数字钥匙DigitalKeymicarConnectionSettings
音频—(走 CarAudioControlManager)micarVolume
IoT—(走 IotClientManager,非 CarProperty)micarIotSettings