04 - 账号同步与多用户体系
维度 D:剖析启动期的账号同步体系 + 多用户体系 入口:
AsyncThreadStartTask.java中的多用户条件块 + 三个 register 方法(:79-150) 关注点:12 个 SyncHandler 注册、NormalRecord 数据闭环、用户切换状态机、车型适配
一、多用户隔离入口
文件:AsyncThreadStartTask.java:79-87
// User 0 和应用 UserId 与当前 UserId 不同时,不用监听账号、刷新自定义驾驶模式数据 5MS
if (mApplication.getUserId() != UserHandle.USER_SYSTEM &&
mApplication.getUserId() == ActivityManager.getCurrentUser()) {
initCustomSyncHandler(); // ① 注册所有同步 Handler
registerNormalRecordDataListenerAndRefreshModeName(); // ② DB 监听 + 刷新驾驶模式
registerAccountsUpdateListener(); // ③ 账号更新监听
initDrivingConfig(); // ④ 驾驶配置初始化
} else {
SteeringModeTypeAdjustHelper.getInstance().release(); // User 0 / 非当前用户释放资源
}为何如此隔离(车机多用户设计):
- User 0 (USER_SYSTEM):系统用户,不绑定具体驾驶员账号,无需同步
- 非当前活动用户的进程:避免后台用户进程干扰当前用户数据
- 仅「非 User 0 且为当前活动用户」的 Settings 进程才注册监听
二、账号同步 Handler 注册清单
2.1 普通 Handler(addCustomNormalHandler)
机制:NormalRecordDataHelper.java:63-67,按 id 精确匹配
public static void addCustomNormalHandler(int id, INormalSettingSyncHandler syncHandler) {
synchronized (NormalRecordDataHelper.class) {
sNormalHandlers.put(id, syncHandler);
}
}| Handler | ID 常量(值) | 同步数据 → 写入 | 源文件 |
|---|---|---|---|
TimeFormatSyncHandler | ID_DISPLAY_TIME_FORMAT (5600) | 时间格式 12/24h → Settings.System.TIME_12_24 | VehicleBodyControl/.../TimeFormatSyncHandler.kt:24-36 |
UIModeSyncHandler | ID_DISPLAY_UI_MODE (5100) | 深色/浅色/自适应 → UiModeManager.nightMode | VehicleBodyControl/.../UIModeSyncHandler.kt:30-70 |
PUICustomSyncHandler | ID_VEHICLE_CONTROL_CUSTOM_PUI (1001) | 右滚轮长按自定义 PUI → Settings.Global.CUSTOM_PUI_FUNC | VehicleBodyControl/.../PUICustomSyncHandler.kt:20-28 |
PUICustomRightButtonSyncHandler | ..._RIGHT_BUTTON (1006) | 右侧自定义按键 → Settings.Global.CUSTOM_PUI_RIGHT_BUTTON | .../PUICustomRightButtonSyncHandler.kt:20-28 |
PUICustomLeftButtonSyncHandler | ..._LEFT_BUTTON (1007) | 左侧自定义按键 → Settings.Global.CUSTOM_PUI_LEFT_BUTTON | .../PUICustomLeftButtonSyncHandler.kt:20-28 |
PromptSoundSyncHandler | ..._PROMPT_SOUND (1005) | 右滚轮提示音 → Settings.Global.CUSTOM_PUI_FUNC_PROMPT_SOUND | .../PromptSoundSyncHandler.kt:20-33 |
PromptSoundRightButtonSyncHandler | ..._RIGHT_BUTTON_PROMPT_SOUND (1008) | 右侧按键提示音 → ..._RIGHT_BUTTON_PROMPT_SOUND | .../PromptSoundRightButtonSyncHandler.kt:20-33 |
PromptSoundLeftButtonSyncHandler | ..._LEFT_BUTTON_PROMPT_SOUND (1009) | 左侧按键提示音 → ..._LEFT_BUTTON_PROMPT_SOUND | .../PromptSoundLeftButtonSyncHandler.kt:20-33 |
ConsolePUICustomSyncHandler1 | ID_..._CONSOLE_PUI (1003) | Console3 自定义 → Settings.Global.CUSTOM_CONSOLE_KEY_3 | .../ConsolePUICustomSyncHandler1.kt:19-27 |
ConsolePUICustomSyncHandler2 | ID_..._CONSOLE_PUI2 (1004) | Console4 自定义 → Settings.Global.CUSTOM_CONSOLE_KEY_4 | .../ConsolePUICustomSyncHandler2.kt:19-27 |
常量定义:base/settingsBaseLib/.../constant/Constants.java:130-259
2.2 SelfMatchIVehicle Handler(addCustomSelfMatchIVehicleHandler)
机制:NormalRecordDataHelper.java:69-73,通过 isMatchRecord() 模糊匹配,onHandRecord() 下发车控信号
public static void addCustomSelfMatchIVehicleHandler(IVehicleSyncHandler syncHandler) {
synchronized (NormalRecordDataHelper.class) {
sMatchingVehicleRecordHandlers.add(syncHandler);
}
}| Handler | 匹配记录类型 | 同步数据 → 下发 | 源文件 |
|---|---|---|---|
DrivingSyncHandler | 驾驶模式(ID_MODE_ONE~TEN/DEFAULT_M1/M2)+ 运动声浪相关 | 运动声浪开关/风格/车内音量/车外音量 → GlobalCarAudioControlManager.setAudioSettings() | micarDrivingSettings/.../DrivingSyncHandler.java:28-163 |
VolumeSyncHandler | 音量相关 ID(6002-6005) | 点按提示音/通知提示音/音量补偿/ANC 降噪 → Settings 或 AudioManager | micarVolume/.../VolumeSyncHandler.kt:21-96 |
两类 Handler 区别:
- 普通 Handler:
sNormalHandlers.get(record.getKey())精确匹配 id,onHandleRecord写 Settings Provider - SelfMatchIVehicle Handler:
isMatchRecord()模糊匹配,onHandRecord通过SettingsCarPropertyManager下发车控信号
三、数据变更 → 刷新闭环
3.1 DB 监听注册
文件:AsyncThreadStartTask.java:113-123
private void registerNormalRecordDataListenerAndRefreshModeName() {
SteeringModeTypeAdjustHelper.getInstance().init(); // 驾驶模式校准助手
ThreadPoolUtils.ASYNC.execute(() -> // 启动时刷新(二次派发,不受 latch)
SteeringModeViewModel.Companion.refreshModeList());
NormalRecordDbManager.registerDataChangeListener((records) -> {
SteeringModeViewModel.Companion.refreshModeList(); // 1.刷新驾驶模式
NormalRecordDataHelper.onUpdateNormalRecordData(records); // 2.下发信号/更新Provider
});
}3.2 数据流图
flowchart TD A["账号云同步数据下行"] --> B["NormalRecordHandler.onDataUpdate"] B --> C["车型适配解算<br/>AccountSyncVehicleAdapterHelper.recoverRecordsKey"] C --> D["缓存本地 DB<br/>NormalRecordDbManager.updateRecordsAndClear"] D --> E["触发监听回调<br/>OnRecordDataChangeListener.onDataUpdate"] E --> F1["SteeringModeViewModel.refreshModeList"] E --> F2["NormalRecordDataHelper.onUpdateNormalRecordData"] F1 --> G1["刷新驾驶模式列表"] G1 --> H1["更新 M1/M2 预设"] H1 --> I1["下发驾驶模式车辆信号"] F2 --> G2["updateSystemSettings<br/>遍历 sNormalHandlers"] F2 --> H2["updateVehicleSettings<br/>遍历 sVehicleRecordHandlers<br/>+ sMatchingVehicleRecordHandlers"] G2 --> K1["INormalSettingSyncHandler.onHandleRecord"] K1 --> L1["写入 Settings Provider<br/>如 TIME_12_24 / CUSTOM_PUI_FUNC"] H2 --> M1["IVehicleSyncHandler.onHandRecord"] M1 --> N1["下发车控信号<br/>SettingsCarPropertyManager.setCarProperty"] classDef src fill:#e1f5fe classDef cache fill:#fff3e0 classDef dispatch fill:#f3e5f5 classDef effect fill:#e8f5e9 class A src class D cache class E dispatch class I1,L1,N1 effect
四、多用户状态机
4.1 账号更新监听
文件:AsyncThreadStartTask.java:125-150
private void registerAccountsUpdateListener() {
AvmSwitchSyncHelper.getInstance().init(mApplication.getUserId());
ProfileSettingsSyncHelper.getInstance();
AccountManager.get(mApplication).addOnAccountsUpdatedListener(accounts -> {
int currentUserId = MiCarUserHandle.myUserId();
int lastUserId = Settings.Global.getInt(
mApplication.getContentResolver(),
SETTINGS_LAST_USER_ID_KEY,
currentUserId); // 首次启动默认给 currentUserId
if (lastUserId != currentUserId) { // 用户切换
SteeringModeViewModel.Companion.asyncClearCustomDrivingModeWhenNoAccount(mApplication);
}
Settings.Global.putInt(mApplication.getContentResolver(),
SETTINGS_LAST_USER_ID_KEY, currentUserId); // 持久化当前用户
ThreadPoolUtils.ASYNC.execute(() -> // 更新 AVM(二次派发)
AvmSwitchSyncHelper.getInstance().updateAvmCarProperties());
}, null, true); // ★ true: 添加后立即回调一次
}
SETTINGS_LAST_USER_ID_KEY(常量,:67)是用户切换检测的持久化状态键。
4.2 用户切换状态图
stateDiagram-v2 [*] --> CheckUserCondition CheckUserCondition --> SkipInit: getUserId()==USER_SYSTEM<br/>(User 0 系统用户) CheckUserCondition --> SkipInit: getUserId()!=getCurrentUser()<br/>(非当前用户进程) CheckUserCondition --> InitSync: 非 User 0 且为当前用户 SkipInit --> [*]: release SteeringModeTypeAdjustHelper InitSync --> InitHandler: initCustomSyncHandler() InitHandler --> RegDB: registerNormalRecordDataListener() RegDB --> RegAccount: registerAccountsUpdateListener() RegAccount --> AccountUpdate: AccountManager 回调<br/>(true 立即触发一次) AccountUpdate --> Compare: 读取 lastUserId vs currentUserId Compare --> NoSwitch: 相等 Compare --> UserSwitch: 不等(用户切换) NoSwitch --> UpdateAVM: updateAvmCarProperties() UserSwitch --> ClearMode: asyncClearCustomDrivingModeWhenNoAccount() ClearMode --> UpdateAVM UpdateAVM --> Persist: Settings.Global.putInt<br/>SETTINGS_LAST_USER_ID_KEY Persist --> [*] note right of CheckUserCondition 多用户隔离: - User 0 不初始化 - 非当前用户进程不初始化 - 仅当前登录用户的进程初始化 end note note right of UserSwitch 切换处理: 1. 清除上一用户自定义驾驶模式 2. NormalRecordDbManager.clearData() 3. refreshModeList() 刷新 end note
4.3 用户切换清除逻辑
文件:SteeringModeViewModel.kt:58-85
fun asyncClearCustomDrivingModeWhenNoAccount(context: Context) {
if (AccountManager.get(context).accounts.isNotEmpty()) return // 已登录账号不清除
val processUserId = MiCarUserHandle.myUserId()
val currentUserId = MiCarActivityManager.getCurrentUser()
if (processUserId != currentUserId) return // 进程用户与当前用户不一致,避免误清除
ThreadPoolUtils.ASYNC.execute {
synchronized(this) {
NormalRecordDbManager.clearData() // 清除本地 DB
}
refreshModeList()
}
}双重保护:① 仅在无账号时清除(有账号则数据可从云端恢复);② 进程用户须等于当前用户(防误清)。
五、关键组件职责
5.1 SteeringModeTypeAdjustHelper(驾驶模式校准)
文件:settingsPage/micarDrivingSettings/.../custom/SteeringModeTypeAdjustHelper.java:29-217
- 自动校准 VCU 与 DCD 的 M1/M2 类型对应(
:94-139):监听TYPE_OF_SPECIAL_MODE_1/2,不一致时重新下发预设 - ACC 时再次下发 M1/M2 预设(
:146-165):休眠唤醒后 VCCD 可能上行异常信号值,Power Mode 切到 ACC 时重新下发 - EventBus 监听(
:203-216):接收CurrentSteeringModeChangeEvent,更新 M1/M2 数据并触发校准
5.2 ProfileSettingsSyncHelper(用车习惯同步)
文件:base/settingsVehicleLib/.../common/ProfileSettingsSyncHelper.kt:20-123
- 监听
ProfileRecordHandler数据变更,分发到 Audio/Vehicle/System 三类设置 - 支持的 Profile ID(
:26-38):如PROFILE_ID_MUSIC_RHYTHM_COLOR→MOOD_LIGHT_RHYTHM_TYPE
5.3 AvmSwitchSyncHelper(AVM 设置同步)
文件:settingsPage/micarDrivingSettings/.../avm/AvmSwitchSyncHelper.java:25-245
- 同步 AVM 相关设置:延迟关闭 / 窄路辅助 / 盲点监测 / 后视影像自动打开
- 将本地 Settings 值写入车辆 Property(
:201-240)
六、车型适配机制
文件:base/settingsVehicleLib/.../accountsync/AccountSyncVehicleAdapterHelper.kt:16-115
不同车型使用不同 Key 前缀(MX11/MS11_P/KUNLUN/MS12/MS13):
- 下行(云→车):
recoverRecordsKey()通过VEHICLE_ADAPTER_RECOVER_KEY解算真实 Key - 上行(车→云):
getVehicleAdapterKey()添加车型前缀
实现车型隔离,同一云同步数据在不同车型映射到不同车控项。
七、上下行数据流路径
下行(云 → 车):
云同步数据 → NormalRecordHandler.onDataUpdate
→ AccountSyncVehicleAdapterHelper.recoverRecordsKey(车型解算)
→ NormalRecordDbManager.updateRecordsAndClear(缓存本地)
→ OnRecordDataChangeListener.onDataUpdate
→ NormalRecordDataHelper.onUpdateNormalRecordData
→ INormalSettingSyncHandler.onHandleRecord (写 Settings Provider)
/ IVehicleSyncHandler.onHandRecord (下发车控信号)
上行(车 → 云):
用户操作 → NormalRecordDbManager.syncNormalData/syncProfileData
→ AccountSyncVehicleAdapterHelper.notifyNormalDataChange(加车型前缀)
→ SyncDataManager.notifyNormalDataChange
→ 云同步服务
八、设计要点
为什么启动期就要注册这些监听?
| 设计点 | 原因 |
|---|---|
| 同步 Handler 注册 | 云同步数据可能在启动后立即下行,需提前备好 Handler |
| DB 数据监听 | 账号切换时需立即刷新驾驶模式,保持 UI 与数据一致 |
| 账号更新监听 | 检测用户切换,触发数据清理和刷新 |
| SteeringModeTypeAdjustHelper.init | 监听车辆信号,实时校准 VCU/DCU,防信号下发失败 |
关键源码行号索引
| 功能 | 文件:行号 |
|---|---|
| 多用户条件判断 | AsyncThreadStartTask.java:79-87 |
| initCustomSyncHandler | AsyncThreadStartTask.java:154-186 |
| registerNormalRecordDataListener | AsyncThreadStartTask.java:113-123 |
| registerAccountsUpdateListener | AsyncThreadStartTask.java:125-150 |
| SETTINGS_LAST_USER_ID_KEY | AsyncThreadStartTask.java:67 |
| 常量定义 | Constants.java:130-259 |
| Handler 分发 | NormalRecordDataHelper.java:80-172 |
| 车型适配 | AccountSyncVehicleAdapterHelper.kt:53-82 |
| 用户切换清除 | SteeringModeViewModel.kt:58-85 |
| 驾驶模式校准 | SteeringModeTypeAdjustHelper.java:94-139 |