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)
  • 使用者:MirrorAdjustControllerNewSteeringWheelAdjustControllerNew

三、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 → 页面跳转(非信号)