03 · 信号流动
标准读写回流链路与全局一致(见
docs/整体架构探索/04-车辆接口层与IPC.md)。本章聚焦 VehicleBody 特有的流动机制。
一、标准链路回顾(两种 UI 形态)
A. 开关型(BaseVehicleNewSwitchPrefController)
用户拨开关
→ handlePreferenceChanged(pref, newValue)
→ [needSecondConfirm? 二次确认 dialog]
→ setCarProperty(isChecked)
→ setVehicleProperty(obtainSetPropertyId(), getPropertyVal(isChecked), areaId)
→ SettingsCarPropertyManager.setCarProperty(...) // 启动超时监控
→ VehicleControlManager.setIntProperty (ASYNC)
→ framework CarPropertyManager → Binder → CarService → HAL
回流: HAL → CarPropertyCacheManager(多路复用) → onPropertyChanged
→ onHandlePropertyChange → preference.setChecked(isPropertyOpen(propVal))
超时/错误: restorePropertyToPrevious → UI 回滚
B. Tab/ButtonToggle 型(基于 VehicleQuickCtrl)
Controller.initLocalVehicleControls() // 硬编码构造 VehicleQuickCtrl 列表
→ CarPropertyMgrPreferenceController.buildTabFromVehicleCtrl(ctrl) // 转 CarUiTabLayout.Button
用户点 Tab
→ ButtonTogglePreference.onTabSelected
→ OnVehicleControlClickListener.onSwitchTabTo(tab, true)
→ onHandleButtonChecked(tab, toState) // 子类决定下什么值
→ VehicleQuickCtrl.onClickListener 内 setVehicleProperty(propId, val, areaId)
回流: onPropertyChanged → queryVehiclePropCtrl(propId, areaId, val) // 三级反查
→ notifyUIByPropertyChange(ctrl, available) → tab.setSelected/setAvailable
→ preference.notifyDataChanged()
VehicleQuickCtrl.genMapKey() 三策略:PROPERTY_ONLY(0) / PROPERTY_AND_AREA(1, "$propId#$areaId") / PROPERTY_AND_VALUE(2)。VehicleAreaQuickCtrl 强制用策略 1。
二、VehicleBody 特有机制 ①:调节心跳
文件:VehicleAdjustHeartBeat.kt(后视镜/方向盘调节用)
调节类功能(后视镜调节、方向盘调节)进入弹窗时,需要持续通知场景应用 com.micar.scene「正在调节」,防止场景侧弹其他弹窗干扰。
class VehicleAdjustHeartBeat(val mAdjustState: AdjustState) {
private val mMainHandler = Handler(Looper.getMainLooper()) {
if (msg.what == MSG_ADJUSTING) {
Intent(ACTION_VEHICLE_BODY_ADJUST).apply {
`package` = "com.micar.scene"
putExtra(ADJUST_MODE, ADJUST_MODE_ADJUSTING) // 1
putExtra(PROPERTY_ID, mAdjustState.propId)
putExtra(PROPERTY_VALUE_BACK_UP, mAdjustState.propValDefault)
}.also { BaseApplication.getGlobalApp().sendBroadcast(it) }
sendEmptyMessageDelayed(MSG_ADJUSTING, 2000L) // 每 2 秒一次,循环到退出
}
}
fun startAdjusting() { mMainHandler.sendEmptyMessage(MSG_ADJUSTING) }
fun stopAdjust() { mMainHandler.removeCallbacksAndMessages(null); /* 发 FINISH 广播 */ }
}- 进入调节弹窗 →
startAdjusting():每 2s 广播com.android.micar.action.VEHICLE_BODY_ADJUSTING+ mode=ADJUSTING(1) - 退出弹窗 →
stopAdjust():广播 mode=FINISH(0) - 使用者:
MirrorAdjustControllerNew、SteeringWheelAdjustControllerNew
三、VehicleBody 特有机制 ②:互斥窗口(active_window)
关键设计:方向盘调节与后视镜调节共用同一个信号 STEERWHEEL_MIRROR_HUD_ADJUST,靠全局配置 active_window 区分谁在用。
// SteeringWheelAdjustControllerNew.kt:43
.setGlobalConfig(GLOBAL_CONFIG_KEY_ACTIVE_WINDOW, WINDOW_STEERING_WHEEL) // "active_window" → 方向盘
// MirrorAdjustControllerNew.kt:51
.setGlobalConfig(GLOBAL_CONFIG_KEY_ACTIVE_WINDOW, WINDOW_xxx) // → 后视镜
.getGlobalConfig(GLOBAL_CONFIG_KEY_ACTIVE_WINDOW) // 读取判断当前谁在用进入弹窗时:下发 STEERWHEEL_MIRROR_HUD_ADJUST = STEERING_WHEEL_ADJUST_PUP_UP(或 EXTR_MIRR_ADJUST_PUP_UP)+ 设置 active_window;退出回 IDLE。两者互斥,同一时刻只能一个在调节。
四、VehicleBody 特有机制 ③:手套箱(密码 + MIS 远程)
最复杂子系统,三条触发路径:
路径 A:本地点开关
GloveBoxController 点击开关
→ 查密码状态:
├─ 无密码 → 直接 GlobalCarPropertyManager.setCarProperty(GLOVE_BOX, OPEN)
├─ 有密码 → 弹 GloveBoxPasswordDialog(PIN 输入)
│ → 验证通过 → 开箱
│ → 错误次数达上限 → 拉车主验证(GloveBoxAccountVerifyUtils)
└─ 监听 DrivingCtrl.CAR_MODE_STATUS(某些车模下禁用)
路径 B:MIS 语音/远程开箱(compartment SDK)
小爱说"打开手套箱" / 手机 App 远程
→ MIS 总线 → GloveBoxNavigationHandler.onAction(Open) [继承 AbsGloveCompartmentHandler]
→ 密码状态判断:
├─ responseCode = 1001 (CODE_OPEN_SUCCESS) → 开箱
├─ 1002 (需密码)
├─ 1003 (类型不支持)
└─ 1004 (密码错误达上限,需车主验证)
→ actionRequest.createResponse().send() // 异步回 MIS
→ 最终仍走 GlobalCarPropertyManager.setCarProperty(GLOVE_BOX, OPEN)
路径 C:密码管理
GloveBoxController 点击"密码设置"
→ GloveBoxSettingsDialogActivity(开关 + 修改密码)
→ GloveBoxPasswordFragment(继承 BasePinPasswordFragment)
→ 验证结果广播 com.android.car.settings.action.PASSWORD_VERIFY_RESULT
密码存储/错误次数/车主验证:GloveBoxPasswordTools / GloveBoxPasswordCheckUtils / GloveBoxAccountVerifyUtils。
GloveBoxModulePublishService(继承AbsModulePublishService)在 manifest 注册为 exported Service,action=com.xiaomi.mis.action.STATIC_DDF_CONFIG_ACTION,permission=com.xiaomi.permission.ACCESS_MIS_SPEC_SERVICE,foregroundServiceType=connectedDevice。
五、VehicleBody 特有机制 ④:调节弹窗流程
后视镜/方向盘调节是 Dialog-style Activity(非 Preference 内联):
SteeringWheelEntryController.onClick
→ 发 POSITION_ADJUST_ACTION + STEERING_WHEEL_ADJUST_DATA
→ 启动 SteeringWheelDialog(按 W-HUD 可选车型 MS11/MS11P/Kepler/KunLun 选 layout)
→ SteeringWheelAdjustControllerNew(FragmentVehicleController)接管:
├─ 进入: setGlobalConfig(active_window) + 下发 PUP_UP + startAdjusting()
├─ 调节: 用户点方向 → 下发 ORIENTATION(1/2/3/4)
└─ 退出: 下发 IDLE + stopAdjust()
后视镜调节同理(MirrorAdjustDialog + MirrorAdjustControllerNew),弹窗内还有 MirrorSelectController 选左/右镜。
六、信号驱动弹窗(非用户点击)
前机盖未关提醒(FrontHoodCloseRemindDialogFragment)——由车辆信号触发,非 Preference 点击:
车辆 HAL 上报 POWER_FRUNK_HOOD_CLOSE_REMIND
→ FrontHoodCloseRemindDialogFragment 监听到信号 → 自动弹出
→ onResume 播警告音(WarningToneUtil)
→ 正向按钮: 下发 HOOD_CLOSE_USER_CONFIRMATION_RESPONSE = DRIVER_CONFIRM_IGNORED(驾驶员确认忽略)
→ 收到 PopUpRemind.NO_REQUEST → finish()
七、外部入口(旁路,非主信号流)
QuickControlCmd(EventBus,页面跳转非信号下发)
语音/深链 → HomePageActivityReal.handlePageSwitchIntent
→ new QuickControlCmd(subPage)
→ EventBus.postSticky
→ EasyEntrySwitchPrefController.handlePageJump(@Subscribe) // 弹出对应二级弹窗
⚠️
QuickControlCmd名字像”快控”但不是信号下发通道,只是页面跳转事件。
IoT 童锁识别(非 CarProperty)
ChildSeatEntryController 注册 IotClientManager.addOnDeviceCardUpdateListener
→ 米家设备列表变化 → 按 model 含 "carseat" 识别儿童座椅
→ 有设备: summary 显示"去智能设备设置"蓝色链接
→ 点击整行: 弹 ChildSeatInstallPositionDialogActivity(下发 CHILD_SEAT_LOCATION FR 信号)
八、流动链路全景图
用户操作
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
开关型(Switch) Tab/Toggle型 调节型(Dialog)
handlePreference- onSwitchTabTo 进入弹窗:
Changed →onHandleButton active_window互斥
→setCarProperty Checked +心跳广播(2s)
↓ →setVehicleProperty +下发PUP_UP
│ │ │
└──────────┬──────────┴─────────────────────┘
▼
SettingsCarPropertyManager (超时监控)
↓
VehicleControlManager → framework CarPropertyManager → HAL
⋮ 回流 ⋮
CarPropertyCacheManager → onPropertyChanged
→ onHandlePropertyChange / notifyUIByPropertyChange → UI 更新
旁路:
① MIS语音/远程 → compartment SDK → GloveBoxNavigationHandler → setCarProperty(GLOVE_BOX)
② 信号驱动 → FrontHoodCloseRemind 自动弹窗
③ IoT → IotClientManager → 童锁设备识别
④ EventBus → QuickControlCmd → 页面跳转(非信号)