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);
    }
}
HandlerID 常量(值)同步数据 → 写入源文件
TimeFormatSyncHandlerID_DISPLAY_TIME_FORMAT (5600)时间格式 12/24h → Settings.System.TIME_12_24VehicleBodyControl/.../TimeFormatSyncHandler.kt:24-36
UIModeSyncHandlerID_DISPLAY_UI_MODE (5100)深色/浅色/自适应 → UiModeManager.nightModeVehicleBodyControl/.../UIModeSyncHandler.kt:30-70
PUICustomSyncHandlerID_VEHICLE_CONTROL_CUSTOM_PUI (1001)右滚轮长按自定义 PUI → Settings.Global.CUSTOM_PUI_FUNCVehicleBodyControl/.../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
ConsolePUICustomSyncHandler1ID_..._CONSOLE_PUI (1003)Console3 自定义 → Settings.Global.CUSTOM_CONSOLE_KEY_3.../ConsolePUICustomSyncHandler1.kt:19-27
ConsolePUICustomSyncHandler2ID_..._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 或 AudioManagermicarVolume/.../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

  1. 自动校准 VCU 与 DCD 的 M1/M2 类型对应(:94-139):监听 TYPE_OF_SPECIAL_MODE_1/2,不一致时重新下发预设
  2. ACC 时再次下发 M1/M2 预设(:146-165):休眠唤醒后 VCCD 可能上行异常信号值,Power Mode 切到 ACC 时重新下发
  3. 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_COLORMOOD_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
initCustomSyncHandlerAsyncThreadStartTask.java:154-186
registerNormalRecordDataListenerAsyncThreadStartTask.java:113-123
registerAccountsUpdateListenerAsyncThreadStartTask.java:125-150
SETTINGS_LAST_USER_ID_KEYAsyncThreadStartTask.java:67
常量定义Constants.java:130-259
Handler 分发NormalRecordDataHelper.java:80-172
车型适配AccountSyncVehicleAdapterHelper.kt:53-82
用户切换清除SteeringModeViewModel.kt:58-85
驾驶模式校准SteeringModeTypeAdjustHelper.java:94-139