22 · 双 flavor 差异 dcddif vs xcddif vs f3dif

核心文件:

  • base/settingsLibAndroid/build.gradle:15-82(productFlavors + sourceSets 挂载点)
  • base/settingsBaseLib/src/main/java/com/android/car/settings/miauto/common/DeviceUtil.java:84,163,226(运行时平台判定)
  • base/settingsLibAndroid/src/{dcddif,xcddif,f3dif}/java/com/android/settingslib/bluetooth/(三套 settingslib 蓝牙实现)
  • settingsPage/micarConnectionSettings/src/main/java/.../MiBluetoothPairingController.kt:283(单份,2956 修复落点)

适用范围:改蓝牙代码前必读。

0. TL;DR

  1. 编译期 flavor 决定走哪一套 settingslib:dcd→dcddif / xcd_cn_a14→xcddif / xcd_cn_a17→f3dif / xcd_global_a17→f3dif+globalbuild.gradle:59-82)。xcd_cn_a17/xcd_global_a17 flavor 编译时使用 f3dif(玄戒 A17 平台),xcddif 是玄戒 A14 历史 flavor。
  2. 运行时 DeviceUtil.isDCDPlatform() 只用 ro.board.platform=="msmnile" 判骁龙 8295(DeviceUtil.java:226-228),没有 isXring()/isKunLunPlatform()isKunLun()(:163) 是车型判定非平台。
  3. 三个 flavor 目录是同名同包不同实现(都是 com.android.settingslib.bluetooth.*),无继承关系src/main/java/com/android/settingslib/bluetooth/ 目录不存在,所有蓝牙类分裂在三 flavor 下。
  4. 本仓 micarConnectionSettings 蓝牙代码绝大多数单份src/main),仅 MiBluetoothCollinear.kt 按 flavor 分(dcddif 独立一份 + xcddif/f3dif 共用一份)。
  5. MANNPROB-2956 修复(commit 7239803be)落在 src/main/.../MiBluetoothPairingController.kt:283,单份代码,所有 flavor 自动同步,无需双改。

1. flavor 选择机制

1.1 flavor → sourceSet 映射(直读 build.gradle:30-82

flavorDimensions=["miPlatform"],4 个 productFlavors:dcd/xcd_cn_a14/xcd_cn_a17/xcd_global_a17

Build Variant主源集flavor 源集平台/版本物理平台
dcdDebugsrc/mainsrc/dcddifA14/A17 通吃骁龙 8295(msmnile)
xcd_cn_a14Debugsrc/mainsrc/xcddifA14玄戒(早期)
xcd_cn_a17Debugsrc/mainsrc/f3difA17玄戒(最新,F3代)
xcd_global_a17Debugsrc/mainsrc/f3dif + src/globalA17 海外玄戒海外版

build.gradle:77-82xcd_global_a17 配置了 src/global/java+src/global/res,但 src/global 目录当前预留暂无内容——海外 flavor 实际只有 f3dif 在生效。

2026-09-06 勘误(第四轮对抗核实):① sourceSets 现为 5 个变体——新增 xcd_global_a14(→xcddif+src/global,海外 A14,build.gradle:71-76),上表应补一行;② f3dif 源集已合入 dev 分支(262 文件被跟踪,随 targetSdk 37 适配 b57402a19 等提交),不再是”A17 分支专属”,本仓 dev 上可直接引用 f3dif 行号;维护者确认 a17 已全面切 dev 交付,a17/xcd/dcd 三机型统一走 dev 分支交付,dev_a17_0610/dev_a17 仅历史参照;③ f3dif CachedBluetoothDevice.java(2719 行)与 dev_a17_0610 同 blob。

1.2 怎么选编译哪一套

./gradlew :app:assembleDcdDebug            # dcddif(骁龙8295)
./gradlew :app:assembleXcd_cn_a17Debug     # f3dif(玄戒A17国内)
./gradlew :app:assembleXcd_global_a17Debug # f3dif+global(玄戒海外)
# xcd_cn_a14 在 A17 主线已不构建(历史 flavor)

1.3 flavor 选择流程图

flowchart TD
  A[Gradle 启动] --> B{选 build variant}
  B -->|dcd| C1[src/main + src/dcddif<br/>骁龙8295]
  B -->|xcd_cn_a14| C2[src/main + src/xcddif<br/>玄戒A14 主线已停]
  B -->|xcd_cn_a17| C3[src/main + src/f3dif<br/>玄戒A17国内]
  B -->|xcd_global_a17| C4[src/main + src/f3dif + src/global<br/>玄戒A17海外 global暂空]
  C1 --> D1[settingslib.bluetooth 来自 dcddif]
  C2 --> D2[settingslib.bluetooth 来自 xcddif]
  C3 --> D3[settingslib.bluetooth 来自 f3dif]
  C4 --> D3
  D1 --> E[micarConnectionSettings 同步按<br/>同名 flavor 挂载 src/dcddif xcddif f3dif]
  D2 --> E
  D3 --> E
  style C3 fill:#ffe,stroke:#a80
  style D3 fill:#ffe,stroke:#a80

2. 平台判定:DCD=骁龙8295,XCD=玄戒

DeviceUtil.java:226-228

public static boolean isDCDPlatform() {
    return PLATFORM_MSMNILE.equals(getPlatform());  // "msmnile",getPlatform()=ro.board.platform(:84)
}
  • isDCDPlatform()==true ⇒ 骁龙 8295;XCD 一律用 !isDCDPlatform() 表达。
  • isKunLun()(:163) 走 device type 通道判车型,≠平台判定。
  • isXring()/isKunLunPlatform(),不要自己读 ro.board.platform,更不要新增这类判定。

3. 三套 settingslib 蓝牙实现差异

3.1 类清单差异(comm -13/diff 直出)

类/能力dcddifxcddiff3dif差异性质
LeAudioProfile-平台能力(LE Audio)
HapClientProfile-平台能力(助听控制)
CsipSetCoordinatorProfile + CsipDeviceManager-平台能力(设备组协调)
VolumeControlProfile-平台能力(音量控制)
LocalBluetoothLeBroadcast + Assistant + Metadata-平台能力(Auracast 广播音频)
BluetoothBroadcastUtils-平台能力
HearingAidAudioRoutingHelper + Info + Constants-平台能力(助听音频路由)
9 个 *Ext.kt(BatteryLevelsInfo/BluetoothEventManagerExt/LeAudioProfileExt/LocalBluetoothManagerExt/LocalBluetoothLeBroadcast*CallbackExt/LocalBluetoothLeBroadcastMetadata/PrivateBroadcastReceiveData/ProfileConnectionState 等)--A17 版本新增(Kotlin 扩展,xcddif 仅 2 个)
DeviceGroupClientProfile / DunServerProfile / VcpProfile--xcddif 独有(f3dif 重构移除)
HearingAidStatsLogUtils.java--xcddif 独有(f3dif 移除)
CachedBluetoothDevice.onBondingStateChanged 签名(int):839(int):1050(int,int prevBondState):1147A17 API 升级

结论

  • dcddif → xcddif/f3dif 的差是平台能力差异(骁龙8295 不支持 LE Audio/Auracast/助听全家桶,玄戒支持)。
  • xcddif → f3dif 的差是Android 版本差异(A14→A17 的 API 签名升级、净增 9 个 *Ext.kt Kotlin 扩展、移除 DeviceGroupClientProfile/VcpProfile/DunServerProfile/HearingAidStatsLogUtils)。
  • 只看 dcddif vs xcddif 会漏掉 A17 实际行为——xcd_cn_a17 flavor 编译的是 f3dif。

3.2 同名同包不同实现(无继承)

三个目录的 CachedBluetoothDevice.java 都是 package com.android.settingslib.bluetooth,类名相同、无继承关系,靠 sourceSet 选哪份编进 APK。不要试图在 dcddif 里 extends xcddif 实现,编译期互不可见。

3.3 三套差异对照

classDiagram
  class dcddif_BT {
    A2dpProfile/HeadsetProfile/HearingAidProfile/Hid/Pan...
    缺 LeAudio/HapClient/CsipSet/VolumeControl/LeBroadcast 全家桶
    note: 骁龙8295 经典蓝牙
  }
  class xcddif_BT {
    全部 dcddif 类
    +LeAudio/HapClient/CsipSet/VolumeControl/LeBroadcast
    +DeviceGroupClientProfile/VcpProfile/DunServerProfile
    +HearingAidStatsLogUtils
    note: 玄戒A14 LE Audio全家桶
  }
  class f3dif_BT {
    全部 xcddif 核心类
    +9个 Ext.kt Kotlin扩展
    -DeviceGroupClientProfile/VcpProfile/DunServerProfile(移除)
    -HearingAidStatsLogUtils(移除)
    onBondingStateChanged(int,int)
    note: 玄戒A17 升级签名+Kotlin化
  }
  dcddif_BT ..> xcddif_BT : 同包同名 不同实现
  xcddif_BT ..> f3dif_BT : 同包同名 A17升级

4. 2956 修复是否需双 flavor 同步?——否(但当前未合入)

MiBluetoothPairingController.ktsettingsPage/micarConnectionSettings/src/main/java/.../bluetooth/单份(所有 flavor 共用)。

⚠️ 当前状态(2026-07-23 二次核实):修复 commit 7239803bedev_a17_0610悬空提交git merge-base --is-ancestor 返回非祖先、git fsck 显示 dangling),已被 reset,当前分支未合入,bug 仍在。实测 onCancel(:283) 仍是旧版(仅 cancelBondProcess),MiBluetoothPairingDialogActivity.kt 也无 mDismissed 标志。

**修复方案(待合入,一旦合入所有 flavor 自动同步)**改两文件(均在 src/main 单份):

  • MiBluetoothPairingController.kt:283 onCancelbondState==BOND_BONDED 判断,已 bonded 走 removeBond(),否则 cancelBondProcess()
  • MiBluetoothPairingDialogActivity.kt dismiss 一次性 + 立即解注册 receiver

f3dif(A17 主线)= 纯上游 AOSP:对抗审查实测 f3dif CachedBluetoothDevice.java 0 个 MICAR-PORTING 标记(xcddif 33 个、dcddif 10 个),无黑名单 isCloseAutoConnectDevice、无 try/catch 广播保护、无半关 ScreenChangedHandler、timeout 不翻倍(用大常量:MAX_UUID 35000 vs xcddif 5000)。即 A17 主线 MICAR 蓝牙定制全部回归原生。改 A17 蓝牙逻辑时不能假设这些定制点存在。

旧文档 docs/连接/蓝牙/02-蓝牙-配对流程.md:19,75,109 仍写 onCancel:279→:281 cancelBondProcess,是 2956 修复前行号,已过时(由 02修订 更新)。

5. 本仓(非 settingslib)蓝牙代码的 flavor 分布

文件位置是否按 flavor 分
MiBluetoothPairingController.kt / MiBluetoothPairingRequest.kt / BluetoothDeviceService.kt / BluetoothScanManager.kt / MiBluetoothUtils.kt / MiBluetoothExt.kt / ConnectionUtils.ktsrc/main单份
MiBluetoothCollinear.ktsrc/{dcddif,xcddif,f3dif}

MiBluetoothCollinear.kt 实测:dcddif 独立一份(20行,含卫星设备 DID_PRODUCT_ID 检测,注释”和产品沟通暂时不显示卫星图片”);xcddif 与 f3dif 内容完全相同(18行,diff exit=0)——即”dcddif 独立 + xcddif/f3dif 共用”两份实现。

实务:改 MiBluetoothCollinear.kt 时 dcddif 单独看、xcddif/f3dif 同步改;改其他本仓蓝牙文件改一处即可。

6. 改蓝牙逻辑时的 flavor 检查清单

flowchart TD
  Start([要改一段蓝牙代码]) --> Q1{改的是 settingslib<br/>com.android.settingslib.bluetooth?}
  Q1 -->|是| Q2{涉及 API 签名?<br/>如 onBondingStateChanged}
  Q1 -->|否 改本仓业务| Q3{文件在 src/main 还是<br/>src/dcddif/xcddif/f3dif?}
  Q2 -->|是 改签名| R1[三套全改:<br/>dcddif单参/xcddif单参/f3dif双参<br/>注意 f3dif 多 prevBondState]
  Q2 -->|否 仅逻辑| R2[涉及 LE Audio/HapClient/CsipSet<br/>则 dcddif 不需要]
  Q3 -->|src/main| R3[改一处即可 所有 flavor 共享]
  Q3 -->|src/flavor| R4[dcddif单独 + xcddif/f3dif同步]
  R1 --> V[跑三种 variant 编译验证:<br/>assembleDcdDebug<br/>assembleXcd_cn_a17Debug<br/>assembleXcd_global_a17Debug]
  R2 --> V
  R3 --> V
  R4 --> V
  V --> End([完成])
  style R1 fill:#fee
  style R4 fill:#fee
  style R3 fill:#efe

实操 checklist

  • 先定位文件find base/settingsLibAndroid/src -name "<类名>" 看出现在哪几个 flavor 目录
  • settingslib 内的类默认三份(main 无蓝牙代码),签名变更必须三套都改
  • 本仓业务代码默认单份(src/main),除 MiBluetoothCollinear.kt
  • 运行时分支用 DeviceUtil.isDCDPlatform(),不要自读 ro.board.platform,不要新增 isXring()
  • CachedBluetoothDevice.java 注意签名:f3dif onBondingStateChanged(int,int)(:1147) 比 dcddif(:839)/xcddif(:1050) 多 prevBondState
  • LE Audio/HapClient/CsipSet/VolumeControl/LeBroadcast 相关:dcddif 没有(平台也不支持),不要硬塞
  • 提交前编译三个 variant./gradlew :app:assembleDcdDebug :app:assembleXcd_cn_a17Debug :app:assembleXcd_global_a17Debug
  • 旧文档行号警惕:02 文档 onCancel:279/:281 已过时

7. 关键结论

  1. xcd_cn_a17/xcd_global_a17 flavor 编译用 f3dif,xcddif 仅 A14 历史;改 A17 蓝牙逻辑看 f3dif。
  2. 平台能力差异(dcddif 缺 LE Audio 全家桶)是硬件/协议栈限制;版本差异(xcddif→f3dif API 签名/Kotlin 化)是软件演进——勿混。
  3. 同名同包不同实现、无继承——sourceSet 编译期叠加,运行时无多态。
  4. 2956 修复7239803be)落单份代码,所有 flavor 自动同步,无”同步另一 flavor”的债。
  5. MiBluetoothCollinear.kt:dcddif 单独 + xcddif/f3dif 同步;改其他本仓蓝牙文件改一处即可。