语言切换知识库 - 对抗审查记录
审查范围:01/02/03/04 共 4 篇文档
审查基准:本仓 /home/zbc/car/MiCarSettings 实际源码
审查人:Claude (对抗审查员)
日期:2026-07-19
共确认问题 13 条,其中:高 4,中 6,低 3。
高严重度
H1 | 04 篇 | 入口 ③ 描述(行 170) | 机制臆造:把 framework LocalePicker 错认成本项目自带的 display.language.LocalePicker
- 文档原文:“
onSelectTabChanged→LocalePicker.updateLocale(locale),调用的是 本项目自带的com.android.car.settings.miauto.display.language.LocalePicker(别和com.android.internal.app.LocalePicker混淆)” - 源码正解:
settingsPage/micarDisplaySettings/src/main/java/com/android/car/settings/miauto/display/LanguagePickerPrefController.java:28import com.android.internal.app.LocalePicker;—— import 的就是 framework 类- 该文件第 46 行
LocalePicker.updateLocale(locale);调用的是 framework 的 updateLocale settingsPage/micarDisplaySettings/src/main/java/com/android/car/settings/miauto/display/language/LocalePicker.java是项目自带类(包名com.android.car.settings.miauto.display.language),但 LanguagePickerPrefController 根本没有引用它(grep 全仓只找到 LocalePickerInjector 在同包内引用)
- 修正建议:改为”调用的是 framework
com.android.internal.app.LocalePicker.updateLocale(与display/language/LocalePicker.java同名但完全不同)“。注意 display/language/LocalePicker.java 真正的入口是它自己的updateLocales(LocaleList)方法,用am.updatePersistentConfiguration(config),见 H2。
H2 | 02 篇 | 入口归一性断言(行 207) | 断言错误:项目内有一处直接调 IActivityManager.updatePersistentConfiguration
- 文档原文:“项目内没有任何一处直接调用
IActivityManager.updatePersistentConfiguration、Settings.System.putString或setprop来改 locale。” - 源码反证:
settingsPage/micarDisplaySettings/src/main/java/com/android/car/settings/miauto/display/language/LocalePicker.java:186 方法签名am.updatePersistentConfiguration(config);public static void updateLocales(LocaleList locales)(line 178),与 framework 走的是另一条写入路径。 - 缓解:此 LocalePicker.java 已被 XML 注释停用(04 篇自己也说”已废弃”),实际不会跑;但断言写得过于绝对。
- 修正建议:把”没有任何一处”改为”除已废弃的
display/language/LocalePicker.java:186外,活跃路径上没有任何一处…”。
H3 | 02 篇 | 整篇大量 framework 行为过于具体,但本仓无法验证 | 应降级为描述性
- 涉及位置(多处):
- §1 三存储点表格(行 12-17):“system_locales 优先级最高 / persist.sys.locale 次高 fallback / Configuration 派生值”
- §2 调用链描述(行 41-58):“AMS.updatePersistentConfiguration() 通过 SettingsProvider.updateSystemSettingLocked() 写 system_locales”、“Android 13 之后 system_locales 是权威落点”
- §4 决策树(行 132-148):“SystemServer 启动 → 读 system_locales → 非空赋 mCurrentConfig / 空 fallback 读 persist.sys.locale”
- §5 时序图(行 167-192):AMS 内部”写 system_locales / 同步 persist.sys.locale”步骤
- §8 实战复盘(行 248-273):基于”system_locales 出厂锁 zh-CN → 走绿色路径 C”的推理
- §9 一句话结论(行 300)
- 问题:以上全部是 AOSP framework 内部行为(
com.android.internal.app.LocalePicker、AMS、SettingsProvider),这些类都不在本仓。文档未明确标注”本仓不可验证”,容易让读者误以为是基于本仓代码得出的结论。 - 修正建议:在 §2 / §4 / §5 顶部加 banner:“以下 framework 链路基于 AOSP android-13.0.0_r53 公开实现描述,本仓不含相关源码,不能从本仓验证。“实战复盘 §8 标注”基于 AOSP 行为推测”。
H4 | 03 篇 | 行 70 + 行 116 | ECE XML preference 数量错误:说”4 个 + 占位”,实际只有 3 个真正的 Preference
- 文档原文:
- 行 70:“ECE 版本只有 4 个 preference(系统版本 / 关于-参数信息 / 开发者选项 + 占位)”
- 行 103 (mermaid):“加载 ECE 精简 XML…只含 4 个 preference”
- 源码正解:
settingsPage/settingsSystem/src/main/res/xml/miauto_universal_settings_fragment_ece.xml共 3 个 Preference:SystemInfoPreference(line 13-18,系统版本)BasicPreference+AboutSettingsPreferenceController(line 31-36,关于-参数&信息)BasicPreference+DeveloperOptionsEntryPreferenceController(line 37-44,开发者选项)
- 修正建议:把”4 个 + 占位”改为”3 个真正的 Preference(系统版本/关于/开发者选项)+ SpacePreference 间距占位”。
中严重度
M1 | 04 篇 | 行 171 表 ④ | 彩蛋”无 region 限制”不严谨
- 文档原文:“国内可用:是(无 region 限制,硬编码
sChangeLanguageEnabled = true)” - 源码正解:
settingsPage/settingsSystem/src/main/java/com/android/car/settings/system/KernelVersionPreferenceController.java:40sChangeLanguageEnabled = true硬编码 ✓- 该 Controller 内部确实无 region 判断 ✓
- 但是!XML 层
settingsPage/settingsSystem/src/main/res/xml/micar_about_settings_fragment.xml:39写明settings:hiddenFeatures="@string/micar_common_settings_lemans_ece",而base/settingsBaseUi/src/main/res/values/strings.xml:91<string name="micar_common_settings_lemans_ece">lemans_ece</string>—— ECE 版本会隐藏内核版本条目,根本点不到。
- 修正建议:改为”代码层无 region 判断、硬编码开启;但 XML
micar_about_settings_fragment.xml:39通过hiddenFeatures在 ECE 版隐藏条目(lemans_ece),ECE 版用户点不到。国内 cn 版正常可见。“
M2 | 03 篇 | 行 110 mermaid 节点 | isGlobalRegion 标签和实际语义混淆,自相矛盾
- 文档原文:mermaid
CheckEU -- "否 → isGlobalRegion=true<br/>(region == 'global' 或其他非 cn/eu/ece 值)" --> GLOBAL_BRANCH - 源码正解:
base/settingsBaseLib/src/main/java/com/android/car/settings/miauto/common/DeviceUtil.java:104-106 即 isGlobalRegion() 仅当 region == “global” 时返回 true;如果 region 是 “fr” / “us” / 任何非 cn/eu/ece/global 值,isGlobalRegion() 返回 false。public static boolean isGlobalRegion() { return BUILD_REGION_GLOBAL.equals(BUILD_REGION); // 仅 "global" == "global" } - 修正建议:节点标签改为”否 → 进入 GLOBAL_BRANCH(含 isGlobalRegion=true 和其他未识别值);isGlobalRegion 仅在 region==‘global’ 时为 true”,避免把”进入 GLOBAL_BRANCH”和”isGlobalRegion=true”等同。
M3 | 03 篇 | 行 91-95 代码片段 | MiCarSettings.isEce() 代码片段漏行
- 文档原文:
// MiCarSettings.java:407-411 public static boolean isEce() { String region = SystemProperties.get(ROM_BUILD_REGION, ""); return TextUtils.equals(region, ECE_REGION); } - 源码正解(
base/settingsBaseLib/src/main/java/com/android/car/settings/miauto/common/views/MiCarSettings.java:407-411):public static boolean isEce() { String region = SystemProperties.get(ROM_BUILD_REGION, ""); MLog.i("isEce() region=" + region); // ← 文档遗漏 return TextUtils.equals(region, ECE_REGION); } - 修正建议:补上
MLog.i(...)一行,或者把行号改为 408+410-411 表示省略。
M4 | 02 篇 | 调用点 #3 描述(行 204) | “toast 提示”描述误导
- 文档原文:“工具类快速切换 toast 提示,二选一切换 zh/en”
- 源码正解(
settingsPage/settingsSystem/src/main/java/com/android/car/settings/utils/LanguageUtils.java:17-44):showTipToast内部实际是弹AlertDialog.MiCarBuilder弹框(builder.setPositiveButton/setNegativeButton/create/show),不是 toast 提示;toast 是切换成功后顺带弹出的反馈(line 27-28)。方法名showTipToast本身具有误导性,但实际是个 Dialog。 - 修正建议:把”toast 提示”改为”确认弹框(AlertDialog.MiCarBuilder,方法名虽叫 showTipToast 但实际弹的是 Dialog)“。
M5 | 02 篇 | 行 226-235 LocaleChangedReceiver 代码切片 | 简化掉 companion object 和 MLog.d
- 文档原文(Kotlin 代码):
class LocaleChangedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (TextUtils.equals(Intent.ACTION_LOCALE_CHANGED, intent?.action)) { VoiceSearchManager.getInstance().onLocaleChanged() } } } - 源码正解(
app/src/main/java/com/android/car/settings/miauto/LocaleChangedReceiver.kt:14-26):- 实际还有
companion object { private const val TAG = "LocaleChangedReceiver" }(line 16-18) - onReceive 第一行是
MLog.tag(TAG).d("Receive locale changed broadcast.")(line 21)
- 实际还有
- 影响:低,简化展示可以接受,但建议加
// 简化展示注释。
M6 | 02 篇 | 行 209 待核实项 | “global flavor 是否有第二条切换路径” 实际已可结案
- 文档原文:“待核实:MiCarSettings 是否在某些 flavor(如 global 海外)有第二条切换路径(例如
IActivityManager直接调用),目前 grep 未发现…” - 实际:经全仓 grep
LocalePicker.updateLocale|updatePersistentConfiguration共 5 处(详见 H2),其中只有display/language/LocalePicker.java:186是am.updatePersistentConfiguration,且该类无任何调用方(已废弃)。所以已可结案:global flavor 没有第二条活跃路径。 - 修正建议:把”待核实”改为”已核实(2026-07-19):全仓仅 5 处写入点,1 处已废弃,无 flavor 私有路径”。
低严重度
L1 | 04 篇 | 行 228 | 中文 strings 行号范围”175-181”不精确
- 文档原文:“中文译文(
values-zh-rCN/strings.xml:175-181):标题’切换语言’,按钮’取消/确定’” - 源码正解(
settingsPage/settingsSystem/src/main/res/values-zh-rCN/strings.xml):- 175:
micar_change_locale_dialog_title“切换语言” - 176: (应该是其他 string,未在文档列出)
- 177:
micar_change_locale_dialog_centent - 178:
micar_change_locale_dialog_btn_cancel“取消” - 179:
micar_change_locale_dialog_btn_confirm“确定” - 180: (其他)
- 181:
micar_setting_changed_language
- 175:
- 影响:低,范围给得太宽。建议改为列出实际行号。
L2 | 01 篇 | 行 110 mermaid 节点 | 列表生成时未提兜底逻辑(仅”命中 currentLocale 的那条 isSelected=true”)
- mermaid 节点:“按固定顺序生成 10 条 LocaleInfoWrapper…命中 currentLocale 的那条 isSelected=true”
- 源码:
SystemLanguageConfig.kt:62-77实际还有”zh+CN 兜底选 zh 那条”逻辑(精确匹配失败时)。 - 影响:低,关键代码切片 §4(行 234-239)已说明兜底。但 mermaid 节点描述不完整。建议补一句”未命中且有 zh-CN 兜底”。
L3 | 04 篇 | 行 170 入口 ③ “Tab 切换中英文” | 描述需要补充上下文
- 文档原文:“
settingsPage/micarDisplaySettings/.../miauto/display/LanguagePickerPrefController.java:34-68+ 自带的display/language/LocalePicker.java:22-208” - 源码正解:
- LanguagePickerPrefController.java 范围 34-68(class 体)✓
- display/language/LocalePicker.java 总行数 208 ✓,但 LanguagePickerPrefController 并未调用它(见 H1),把它列为”实现要点”是误导。
- 影响:中-低,与 H1 是同源问题。建议删掉”+ 自带的
display/language/LocalePicker.java:22-208”或注明”该类无活跃调用方”。
4 篇之间的一致性检查
通过 ✓
- region prop key
ro.micar.build.region:03 篇(行 15)、04 篇(行 13/54)一致,均与DeviceUtil.java:29+MiCarSettings.java:179吻合。 - 入口 ① 移除逻辑行号 72-78:03 篇(行 152
:69-80)、04 篇(行 270:72-78)一致。 - LocaleChangedReceiver 注册位置
AndroidManifest.xml:1583-1589:02 篇(行 215)、04 篇(行 159)一致。 - 4 个 LocalePicker.updateLocale 调用点(02 篇 §6 表)vs 4 个入口(04 篇 §4 表):组织方式不同但实质对应。
system_universal_language_title/system_universal_language_switch_confirm文案:01 篇表格与源码 strings.xml:325-326 / zh-rCN:295-296 完全一致。- 彩蛋 5 个 strings 文案:04 篇 §4.3 与源码 strings.xml:204-209 完全一致。
不通过(已计入上面)
- 02 篇”4 个调用点归一”vs 04 篇”4 个入口”——本身一致,但 02 篇漏了 display/language/LocalePicker.java:186 这条 IActivityManager 直调(H2)。
最该修正的 3 件事(按 ROI 排序)
-
【必修】04 篇入口 ③ 描述(H1 + L3):删掉”调用的是本项目自带的 display.language.LocalePicker”这一段。LanguagePickerPrefController 调的是 framework 类,display/language/LocalePicker.java 与它无关。这是机制臆造,会直接误导后续维护者。
-
【必修】02 篇入口归一性断言(H2 + M6):把”项目内没有任何一处直接调用 IActivityManager.updatePersistentConfiguration”改为”活跃路径上没有…(已废弃的 display/language/LocalePicker.java:186 是唯一一处)“,同时把”待核实 global flavor 第二路径”结案。
-
【强烈建议】02 篇 framework 行为加不可验证标注(H3):在 §2/§4/§5/§8 顶部统一加 banner:“以下 framework 链路基于 AOSP 公开实现描述,不在本仓”。这能让读者清楚区分”本仓已验证”与”AOSP 行为推断”,避免今后踩坑时把推断当事实。