手套箱 05 · 埋点体系

本篇回答:开箱相关埋点有哪些、每个字段的语义与来源代码行、物理按键(Meego 5458412)如何扩展 source 维度。

基线声明:行号默认以 dev 基线工作区 /home/zbc/car/fuben4_micarsettings/MiCarSettings-5458412(origin/dev @ 930be9145)为准;出货对照 /home/zbc/car/pack_a17_cn_20260923;平台源码 /home/zbc/pangu/xcd_xring_a17_fermi/vendor/micar。本文全部 file:line 均已按基线逐个打开核实。

埋点引入的权威参考 change 研究笔记:参考change-368053.md(本库外,路径以 Meego 目录为准)。

1. 事件清单与字段语义

手套箱相关埋点共 3 个,全部封装在 VehicleControlReporter(base/settingsBaseLib/src/main/java/com/android/micar/settings/track/VehicleControlReporter.kt),事件 ID(TIP)与参数 key 常量在 TrackConstants.java。

1.1 打开手套箱 TIP_OPEN_GLOVE_BOX

维度值来源
事件 ID1147.1.0.1.54662TrackConstants.java:308-309
事件类型Event.CLICK(“click”)VehicleControlReporter.kt:498;枚举定义 Event.kt:9
参数 1type(key=KEY_GLOVE_BOX_OPEN_TYPE)key 定义 TrackConstants.java:557-558
参数 2tab_instance_id(key=KEY_TAB_INSTANCE_ID)key 定义 TrackConstants.java:535-536
唯一调用点GloveBoxController.kt:237(reportGloveBoxOpenTrack 内)全仓 grep 实证,无第二调用点

type 取值(VehicleControlReporter.kt:104-108,TrackValues 内):

/** 打开方式-无密码 */
const val GLOVE_BOX_OPEN_NO_PASSWORD = 1
/** 打开方式-输密码 */
const val GLOVE_BOX_OPEN_WITH_PASSWORD = 2

语义:type 是”密码维度”——本次开箱是否经过密码/账号验证,不是”触发来源”。reportOpenGloveBox(type) 自身不推断来源,只把调用者传入的 type 原样写入参数(VehicleControlReporter.kt:495-501)。

调用侧的三条业务路径分别写入:

业务路径传入 type代码位置
无密码直开GLOVE_BOX_OPEN_NO_PASSWORD(默认参数)GloveBoxController.kt:222(默认形参)+ :268(无密码分支调 openGloveBox())
密码验证通过GLOVE_BOX_OPEN_WITH_PASSWORDGloveBoxController.kt:201
密码错误达上限→车主账号验证通过GLOVE_BOX_OPEN_WITH_PASSWORDGloveBoxController.kt:215

注意:账号验证兜底路径开箱也记为 type=2——type=2 的准确含义是”经过某种验证后打开”,不区分密码验证还是账号验证。

1.2 成功忘记手套箱密码 TIP_GLOVE_BOX_FORGOT_PASSWORD_SUCCESS

维度值来源
事件 ID1147.0.0.0.54663TrackConstants.java:310-311
事件类型Event.EXECUTE(“execute”)VehicleControlReporter.kt:513;枚举定义 Event.kt:12
参数仅 tab_instance_id,无业务参数VehicleControlReporter.kt:514
调用点 1GloveBoxController.kt:248 → 触发于 :210-219 showAccountVerifyPage 成功回调(锁定→账号验证→清零错误次数并开箱)—
调用点 2GloveBoxSettingsDialogActivity.kt:262 → 触发于 :193-205 showAccountVerify 成功回调(设置页内锁定→账号验证→清零+清除密码)—

两个调用点是两条不同业务路径各报一次,不是同一流程的重复上报:Controller 侧验证成功后直接开箱,DialogActivity 侧验证成功后清除密码留在设置页。368053 评审(PS4 Skill Review)曾要求确认此处不重复,结论是流程分叉、各自一次(见 参考change-368053.md「评审关注点」第 2 条)。

1.3 附:密码操作 TIP_GLOVE_BOX_PASSWORD_OPERATION

与开箱不同事件,但同属手套箱埋点族,防止与开箱 type 混用:

维度值来源
事件 ID1147.0.0.0.54664TrackConstants.java:312-313
事件类型Event.EXECUTEVehicleControlReporter.kt:528
参数type(key=KEY_GLOVE_BOX_PASSWORD_TYPE):1=设置密码成功 / 2=关闭密码成功 / 3=修改密码成功key TrackConstants.java:559-560;枚举 VehicleControlReporter.kt:109-114
调用点GloveBoxSettingsDialogActivity.kt:160(SET)/:179(CLEAR)/:130(CHANGE)—

key 冲突警示:开箱的 KEY_GLOVE_BOX_OPEN_TYPE 与密码操作的 KEY_GLOVE_BOX_PASSWORD_TYPE 字符串值都是 "type"(TrackConstants.java:557-560),后视镜/天幕等事件也复用 "type"/"status"。每个 TIP 的参数 HashMap 独立、上报时不会互相覆盖,但数据分析侧按 key 聚合时语义会混合,新增字段时优先选不复用的 key 名。

1.4 口径边界:MIS 语音/远程开箱不产生 54662

GloveBoxNavigationHandler.openGloveBox()(GloveBoxNavigationHandler.kt:125-131)直接调 setCarProperty 写开箱属性,没有任何埋点调用。即语音/远程入口开箱不上报 TIP_OPEN_GLOVE_BOX,数据分析口径上 54662 只覆盖设置页软按键路径。做物理按键埋点验收时同样要明确:新来源是挂进 Controller 统一上报点,还是另起炉灶——前者才能复用现有口径。

2. type 与 source 的正交语义

现状:手套箱事件当前不带 source 字段。reportOpenGloveBox 只写 type + tab_instance_id(VehicleControlReporter.kt:499-500)。

但 source 维度的基础设施已经存在:

  • KEY_SOURCE = "source" 常量:TrackConstants.java:533-534(VehicleControlKey 内,注释”访问来源”);
  • 取值枚举:SOURCE_SETTINGS = 1(设置点击进入)/ SOURCE_OTHER = 2(其他),VehicleControlReporter.kt:38-42;
  • 使用惯例样例:reportEnterMirrorAdjust(source)(VehicleControlReporter.kt:143-149)与 reportEnterSteeringWheelAdjust(source)(:191-201)——都是”进入调节页面”类事件带 source。

如实声明协议待定状态:后视镜/方向盘的 source 惯例只能证明”Reporter 层支持 source 参数、常量已定义”,不能证明数据平台侧手套箱事件已注册 source 枚举。368053 研究笔记的可复用设计要点第 1 条明确:若 PRD 要复用 type 表达物理来源,必须先取得数据平台枚举定义,不能自行造 type=3;若正交扩展,则新增独立 source 参数。两种路线都需要数据需求方书面确认协议后再动手。

3. 上报链路与时机

3.1 链路

reportOpenGloveBox 内部(VehicleControlReporter.kt:495-505):

fun reportOpenGloveBox(type: Int) {
    try {
        MLog.i("$TAG reportOpenGloveBox type=$type")
        TrackEvent.create(Event.CLICK, VehicleControlTips.TIP_OPEN_GLOVE_BOX)
            .addParam(VehicleControlKey.KEY_GLOVE_BOX_OPEN_TYPE, type)
            .addParam(VehicleControlKey.KEY_TAB_INSTANCE_ID, getTabInstanceId(TabConstants.VEHICLE_CONTROL))
            .report()
    } catch (e: Exception) { ... }
}

逐层去向:

  1. TrackEvent.report()(TrackEvent.kt:32-37):把 TIP 放进 mParams[KEY_TIP](KEY_TIP="tip",TrackConstants.java:379),调 SettingsTrack.track(event.eventName, mParams);
  2. SettingsTrack.track()(SettingsTrack.kt:72-74):直接转发 mOneTrack.track(event, params);
  3. 终点是小米 OneTrack 埋点 SDK(com.xiaomi.onetrack.OneTrack),实例参数:APP_ID=31000401514(SettingsTrack.kt:12)、PROJECT_ID=xiaomiev-cockpit-intl(:16)、Mode.APP(:35)。即埋点数据进 OneTrack 管道,不在本地落盘。

3.2 时机:写完属性即报,不等回执

统一开箱点(GloveBoxController.kt:222-229):

private fun openGloveBox(openType: Int = TrackValues.GLOVE_BOX_OPEN_NO_PASSWORD) {
    MLog.tag(TAG).d("openGloveBox openType=$openType")
    setVehicleProperty(
        MiCarPropertyIds.DrivingCtrl.GLOVE_BOX,
        CommonParams.OpenClose.OPEN
    )
    reportGloveBoxOpenTrack(openType)
}

setVehicleProperty() 之后立即上报,没有等待开箱成功结果或属性回读。所以 54662 的语义是”用户开箱操作/意图已受理”,不能据此宣称”手套箱已物理打开成功”。物理按键需求验收埋点时应按此既有语义对齐,或先与数据需求方确认是否要改为结果事件。

3.3 异常隔离

reportGloveBoxOpenTrack(GloveBoxController.kt:235-241)与 reportOpenGloveBox(VehicleControlReporter.kt:496-504)双层 try-catch,失败只记日志不打断车辆控制。但**“没崩”≠“生效”**——验收必须看 reportOpenGloveBox type=... 日志与 OneTrack 实际数据,不能只验证主流程正常。

4. 演进史(三笔参考 change)

change状态一句话定位
161208(Meego 2350608)MERGED软按键开箱与密码流程主参考:建立”已开启提示→无密码直开→密码校验→错误上限转车主验证→统一 setVehicleProperty 开箱”业务链,物理按键应在此链上游汇入而非旁路。
165854(N801PROB-34263)MERGED不可用原因 Toast 映射参考:建立 Peripheral.GLOVE_BOX_UNAVL_REASON 读取与”未解防/防玩/代客”文案映射,物理按键不可用提示直接复用(现 GloveBoxController.kt:288-302)。
368053(Meego 3860784)MERGED(PS14)开箱埋点引入 change:新增 VehicleControlReporter 与 25 个车辆控制事件,reportOpenGloveBox 自此存在;确立”type=密码维度、Reporter 不推断来源、tabInstanceId 由 Reporter 内部取”三原则。

三笔均在当前基线祖先链中(各研究笔记已用 git merge-base --is-ancestor 实证)。

5. 物理按键扩展设计(source 穿透)

前置:协议路线先按第 2 节与数据方确认。以下为采用”独立 source 参数”路线时的工程约束,详细方案见 07-物理按键扩展设计.md。

5.1 扩展点选择

唯一合规挂点是 GloveBoxController.openGloveBox(:222-229)——三条软按键路径已在此收敛,物理按键汇入后同样走这里,最终开箱点只报一次。禁止在信号回调里单独打一次”物理按键按下”开箱事件再在开箱点打一次,那会重复计数。

5.2 source 必须不可变捕获穿透异步回调

软按键路径有两段异步:密码验证回调(:198-202)与账号验证回调(:212-218),Kotlin 闭包捕获的是当时局部 val。物理按键扩展时,source 应以同样方式作为不可变参数逐层传递:

  • 入口(软按键点击 / GlvBoxBtnSts 物理信号回调)确定 source 后,作为 val 传入 showPasswordCheckDialog/showAccountVerifyPage 的回调闭包;
  • 回调内调 openGloveBox(openType, source);
  • 禁止在 Controller 上挂 private var currentSource 之类共享可变状态:软/物理并发时,后到的触发会覆盖先到的 source,先触发那笔的异步回调完成后会拿错来源上报。

5.3 并发口径:后请求为准

软按键与物理按键几乎同时触发时,业务上以后到的请求覆盖前者(属性写是幂等的 OPEN,埋点以后请求携带的 source 为准),这正是 5.2 禁止共享可变 source 的原因——共享变量天然实现”后请求覆盖”,但代价是先请求的回调跑完后又拿新 source 补报,产生错单。不可变捕获下,后请求只影响自己的回调链,先请求的链若最终也走到 openGloveBox,报的是自己入口的 source;若产品要求严格”只认后请求”,需在入口侧加请求序号/时间戳仲裁,这属于 07 篇的设计内容。

5.4 验收矩阵

路径预期 type预期 source备注
软按键×无密码1设置现有行为回归
软按键×密码/账号验证2设置现有行为回归
物理×无密码1物理(枚举值待数据方定义)新增
物理×密码/账号验证2物理来源必须穿透两段异步回调
MIS 语音/远程(不上报)—口径边界,见 1.4

验收以 OneTrack 数据 + reportOpenGloveBox type=... 日志双凭证为准。

6. tabInstanceId 陷阱:物理入口的 UUID 可能是历史值

机制(TabSwitchUuidMap.kt):

  • put(tabId)(:21-27):每次 tab 切换生成新随机 UUID 存入 map,重复切换覆盖;
  • get(tabId)(:35-38):不存在返回 null,存在则返回最近一次切换的 UUID;
  • Reporter 侧:getTabInstanceId(TabConstants.VEHICLE_CONTROL)(VehicleControlReporter.kt:21-22),VEHICLE_CONTROL=1(TabConstants.kt:10)。

陷阱:物理按键可发生在非设置页。那一刻 VEHICLE_CONTROL 的 tabInstanceId 有三种可能:从未切换过→null;N 分钟前看过车辆控制页→历史 UUID;刚从该页离开→看似”当前”的 UUID。无论哪种,它都不代表”本次物理触发会话”。

368053 评审曾把”Controller 传空 tabInstanceId”改为”Reporter 内部按 tab 取 UUID”,但那是为页面内触发设计的,物理入口不在该假设内。验收原则:只记录实际值,不解释语义——测试时如实记录每次物理触发上报的 tab_instance_id 是 null 还是某 UUID,交给数据需求方决定口径(接受/置空/另设物理会话 ID),开发侧不擅自”修正”。

7. 埋点调用链

两个入口(软按键点击 / 物理按键信号)汇入共享密码流程,收敛到统一上报点:

flowchart TD
    A["软按键点击<br/>GloveBoxController.showGloveBoxSwitchClick :261"] --> B{密码状态}
    P["物理按键信号 GlvBoxBtnSts(待接入)<br/>携带 source=物理,不可变捕获"] --> B
    B -->|"已打开"| C["Toast 提示,不上报 :263"]
    B -->|"无密码"| D["openGloveBox(type=1) :222"]
    B -->|"错误达上限"| E["showAccountVerifyPage :210<br/>账号验证回调捕获 source"]
    B -->|"已设密码"| F["showPasswordCheckDialog :196<br/>密码回调捕获 source"]
    F -->|"验证通过"| G["openGloveBox(type=2) :201"]
    E -->|"验证通过"| H["openGloveBox(type=2) :215<br/>+ reportGloveBoxForgotPasswordTrack :216"]
    D --> I["setVehicleProperty(GLOVE_BOX, OPEN)<br/>:224-227"]
    G --> I
    H --> I
    I --> J["reportGloveBoxOpenTrack :235<br/>统一上报点,只报一次"]
    J --> K["VehicleControlReporter.reportOpenGloveBox :495<br/>CLICK + TIP 54662 + type + tab_instance_id"]
    K --> L["TrackEvent.report → SettingsTrack.track<br/>→ OneTrack SDK(APP_ID 31000401514)"]
    H -.->|"并行"| M["TIP 54663 EXECUTE<br/>忘记密码成功"]

证据清单

文件行号区间说明
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/VehicleControlReporter.kt38-42SOURCE_SETTINGS=1/SOURCE_OTHER=2 来源枚举(手套箱未用)
同上104-114手套箱 type 枚举:开箱 1/2、密码操作 1/2/3
同上143-149, 191-201后视镜/方向盘进入事件带 KEY_SOURCE 的既有惯例
同上489-505reportOpenGloveBox(type):CLICK+54662,原样写 type
同上507-519reportGloveBoxForgotPasswordSuccess():EXECUTE+54663,仅 tab_instance_id
同上521-535reportGloveBoxPasswordOperation(type):EXECUTE+54664
同上21-22getTabInstanceId 委托 TabSwitchUuidMap.get
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/TrackConstants.java308-313三个手套箱 TIP 事件 ID
同上379KEY_TIP="tip"
同上532-536VehicleControlKey.KEY_SOURCE/KEY_TAB_INSTANCE_ID
同上557-560KEY_GLOVE_BOX_OPEN_TYPE/KEY_GLOVE_BOX_PASSWORD_TYPE 均为 "type"
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/TrackEvent.kt32-37report() 写入 tip 后转 SettingsTrack.track
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/SettingsTrack.kt12, 16, 30-38, 72-74OneTrack 实例参数(APP_ID/项目/Mode.APP)与 track() 转发
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/Event.kt7-21事件类型枚举(CLICK/EXECUTE 等)
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/TabSwitchUuidMap.kt21-38UUID put 覆盖 / get 返回历史值或 null
base/settingsBaseLib/src/main/java/com/android/micar/settings/track/TabConstants.kt10VEHICLE_CONTROL = 1
settingsPage/VehicleBodyControl/.../glovebox/GloveBoxController.kt196-203密码验证成功回调,openGloveBox(WITH_PASSWORD)
同上210-219账号验证成功回调:清零+开箱+忘记密码埋点
同上222-229统一开箱点:先写属性即上报,不等回执
同上235-252两个埋点包装方法,双层 try-catch 异常隔离
同上261-278软按键点击入口三分支
同上288-302不可用原因→Toast(165854 遗产,物理按键复用)
settingsPage/VehicleBodyControl/.../glovebox/GloveBoxSettingsDialogActivity.kt130, 160, 179密码操作埋点三调用点(CHANGE/SET/CLEAR)
同上193-205, 249-266设置页内忘记密码成功路径与埋点包装
settingsPage/VehicleBodyControl/.../glovebox/GloveBoxNavigationHandler.kt125-131MIS 语音/远程开箱直写属性,无埋点(口径边界)
下载/test/车机Feature/Meego 5458412/kb/参考change-368053.md全文开箱埋点演进与评审关注点(重复上报/key 冲突/tabInstanceId)
同目录 参考change-161208.md / 参考change-165854.md篇头软按键流程 / 原因 Toast 两笔前序 change 定位