69 · dcddif 版全览 — 1456 行小白导览与三方改动对照

源码基线:dcddifbase/settingsLibAndroid/src/dcddif/java/com/android/settingslib/bluetooth/CachedBluetoothDevice.java,dev 分支,共 1456 行)。 本系列 61-68 章的基线是 f3dif(2719 行,A17 主线纯 AOSP);本章反过来以 **dcddif(dcd flavor,骁龙平台)**为基线做一篇自成一体的入门导览——先整体后细节,不要求先读完 61-68。 交叉引用:60-总览与学习地图61-一台蓝牙设备的Android身份22-双flavor差异70-active设备闭环篇

0. 这个文件是什么来头

CachedBluetoothDevice = 车机上一台蓝牙设备的”档案卡 + 遥控器”

  • 档案卡:缓存这台设备的名字、电量、连接状态、支持哪些 profile(内存里,免跨进程反复查询)
  • 遥控器:对外提供连接、断开、配对、取消配用的操作入口

dcddif 版文件头有三段”血统”,改代码前必须能分辨:

来源证据(行号)内容
Android 原生 (AOSP):1-15 Apache 协议settingslib 蓝牙模块核心类,与手机设置同源
高通 (Qualcomm):17-52 BSD 协议双蓝牙改造,代码内 [Dual Bluetooth] 标记
小米 (MICAR)MICAR-PORTING-START/END 标记(dcddif 共 10 行标记)车机定制:自动连接黑名单、HID 配对走默认适配器、超时翻倍

dcddif 是什么:不是笔误。settingsLibAndroid/build.gradle:49-50src/dcddif/javasrc/dcddif/res 并入 dcd flavor 的源码集——即”国内骁龙平台差异化代码”的存放位置。三 flavor 机制见 22 篇

1. 预备概念(30 秒版,详细见概念篇)

概念一句话展开
BluetoothDevice系统对象,代表一台远端设备,MAC 唯一标识;每次查询跨进程10 篇
bond(配对/绑定)BOND_NONE → BOND_BONDING → BOND_BONDED,先配对才能连接11 篇
profile蓝牙按用途分的通道:A2DP 放歌 / HFP 通话 / PBAP 电话簿 / MAP 消息……”连接手机”= 逐条 profile 建连12 篇
UUID设备”我支持哪些功能”的清单,配对早期可能拿不到 → 大量”晚点再连”逻辑10 篇
为什么叫 Cached状态缓存在本进程 + 事件推送更新 + 统一通知 UI——类的设计主线61 篇

2. 字段地图(:103-158)

┌─ 身份 ─────────────────────────────────────────────┐
│ mDevice        本设备对应的系统 BluetoothDevice      │
│ mActiveDevice  双蓝牙下"当前实际操作"用的设备对象(高通) │
│ mLocalAdapter  默认蓝牙适配器(车机蓝牙芯片1)         │
│ mActiveAdapter 当前使用的适配器(可能是芯片2,高通)    │
│ mRssi          信号强度(越接近0越强)                │
│ mHiSyncId      助听器左右耳配对组 ID(:121)          │
│ mSubDevice     助听器的"另一只耳朵"(:156)           │
├─ 能力 ─────────────────────────────────────────────┤
│ mProfiles        这台设备支持的 profile 列表(:126)   │
│ mRemovedProfiles 被移除的 profile(:129)             │
├─ 状态 ─────────────────────────────────────────────┤
│ mIsActiveDeviceA2dp/Headset/HearingAid  是否当前活跃  │
│ mIsXxxProfileConnectedFail             连接是否超时   │
│ mJustDiscovered / mUnpairing / mConnectAttempted     │
├─ 基础设施 ──────────────────────────────────────────┤
│ mHandler       主线程 Handler=连接超时看门狗(:160)   │
│ mCallbacks     观察者列表(:136)                     │
│ mDrawableCache 设备图标 LruCache(:158)              │
│ mProfileLock   保护 mProfiles 的锁(:118)            │
└─────────────────────────────────────────────────────┘

三个值得注意的设计:

  • CopyOnWriteArrayList(:126、:136):读多写少的线程安全容器。UI 线程频繁遍历,蓝牙回调线程偶尔增删。
  • 一个 Handler 三种消息(:160-180):msg.what 直接复用 profile 数字 ID(BluetoothProfile.A2DP/HEADSET/HEARING_AID),收到消息=“该 profile 连接超时”。
  • mDevice vs mActiveDevice 两个设备对象:双蓝牙架构下同一 MAC 在两个栈里各有一个 BluetoothDevicemDevice=档案归属,mActiveDevice=实际发命令用的那个。

3. 方法分组地图

分组代表方法(dcddif 行号)一句话
构造初始化构造器 :182、fillData :495、updateProfiles :737出生时填档案:UUID→profile 集合、活跃位、权限迁移
连接/断开/配对connect :359、connectAllEnabledProfiles :387、disconnect :323/:337、startPairing :440、unpair :461遥控器的按钮
事件入口(被回调)onProfileStateChanged :221、onUuidChanged :788、onBondingStateChanged :839、onActiveDeviceChanged :640、onBluetoothStateChanged :292档案的更新来源,事件驱动
状态查询isConnected :705、isBusy :724、getBondState :625、isActiveDevice :678档案卡的读法
UI 文案getConnectionSummary :1045、getCarConnectionSummary :1164(车机专用)界面副标题的算法
观察者registerCallback :887、dispatchAttributesChanged :895变化通知 UI
排序compareTo :922已连接 > 已配对 > 刚发现 > 信号强 > 名字序
双蓝牙(高通)initAdapterDevice :1379、unpairCounterpartDevice :1436、getCounterpartDevice :1427同一 MAC 双栈路由

4. 三个必懂机制

4.1 连接超时看门狗(onProfileStateChanged :233-261 + mHandler :160-180)

STATE_CONNECTING   → mHandler 发 60s 延时消息(what=profileId,MAX_MEDIA_PROFILE_CONNECT_DELAY=60000,:111)
STATE_CONNECTED    → removeMessages 撤看门狗(60s 内连上了,正常)
STATE_DISCONNECTED → 看门狗还在 = 连了 60s 没连上就被断 → 移除消息 + 置失败标志位
消息落地(60s 到)   → handleMessage 置 mIsXxxProfileConnectedFail=true + refresh()
                      → UI 走 profile_connect_timeout_subtext 显示"连接超时"(:1053-1055)

4.2 UUID 晚到再连(connectAllEnabledProfiles :387 + onUuidChanged :788)

配对刚完成时 UUID 清单可能没同步到,mProfiles 为空时直接 return 不连(:390-399,注释写明防与车机配对的竞态);等 ACTION_UUID 事件到来,onUuidChanged 检查时间窗(普通 10s / HOGP 60s / 助听器 30s,小米把窗口全部 ×2,:793-799)内尝试过连接就补连。

4.3 配对完成自动连 + 小米黑名单(onBondingStateChanged :839-860)

BOND_BONDED 且 isBondingInitiatedLocally()(本机主动发起)→ 自动 connect();小米在此插入 isCloseAutoConnectDevice()(:821-837):读 Settings.Securebluetooth_close_autoconnect(只存一个 MAC),命中则不自动连。 ⚠️ f3dif(A17)没有这个判断——CarPlay 黑名单失效问题,详见 66 §自动回连31 §4

5. 完整流程串讲:用户在车机上点”连接我的手机”

1. UI 层(micarConnectionSettings 的 preference/controller)调 cachedDevice.connect()
2. ensurePaired()(:431)── 未配对则先 startPairing() 并返回
3. connectAllEnabledProfiles()
   ├─ mProfiles 为空(UUID 未到)→ 暂不连,等 onUuidChanged
   └─ 否则 mActiveAdapter.connectAllEnabledProfiles(mDevice)
4. 蓝牙栈逐条 profile 建连,状态变化 → 广播
5. BluetoothEventManager 收广播 → LocalBluetoothProfileManager
   → onProfileStateChanged(CONNECTING → 发看门狗 / CONNECTED → 撤看门狗)
6. dispatchAttributesChanged()(:895)→ Callback.onDeviceAttributesChanged()(:944-946)
7. UI preference 收到回调重读 getName()/isConnected()/MiCar 三态摘要(getDeviceSummary,
   MiCarBluetoothBondedDevicePreference.kt:92-108)→ 刷新
   ⚠️ 注意:AOSP 的 getCarConnectionSummary()/getConnectionSummary() 在本仓是零调用死链,
   车机 UI 用的是 MiCar 自建三态,详见 74 章

卡在 CONNECTING 60 秒 → 看门狗触发 → 失败标志位 → UI 显示连接超时。

6. 三方改动对照表(改 dcddif 前必读)

代码段行号归属内容
initAdapterDevice():1379-1405高通双蓝牙同一 MAC 双栈路由,决定 mActiveAdapter/mActiveDevice
unpairCounterpartDevice():1436-1447高通双蓝牙解绑时把另一个栈里的同一设备也解掉
isNewAdapterRequired():1452-1455高通双蓝牙写死 return true(TODO 说要从 Settings 读开关)
startPairing() HID 分支:442-449小米HID 设备(键盘/手柄)强制走默认适配器配对
isCloseAutoConnectDevice():819-837小米自动连接黑名单(Settings.Secure bluetooth_close_autoconnect,单 MAC)
onUuidChanged() 窗口×2:792-800小米自动重连窗口翻倍

实践提醒MICAR-PORTING[Dual Bluetooth] 圈住的逻辑是平台定制的,动它们易在 flavor 间产生行为分歧;上游更新时这些补丁要手工合并。本文件在 dcddif 源码集——dev 分支 src/ 下现存 dcddif / xcddif 两份副本(build.gradle:46-67:dcd 用 dcddif,xcd 与 global 共用 xcddif;f3dif 仅是 A17 分支的历史基线,本分支不存在)。改 dcddif 后须确认是否同步改 xcddif 同名文件(检查清单见 22 篇)。

7. 在本工程里谁在用它

由同目录 CachedBluetoothDeviceManager 创建持有;settingsPage/micarConnectionSettings 大量消费(grep 核实):

  • controller/BluetoothBondedDevicesPrefController.java — 已配对设备列表
  • preference/MiCarBluetoothBondedDevicePreference.kt — 单个设备条目(注册 Callback 刷新自己)
  • view/MiBluetoothPairingDialogActivity.kt — 配对弹窗
  • view/BluetoothDisconnectConfirmDialogFragment.java — 断开确认
  • controller/BluetoothOperationsController.java — 连接/断开操作中枢

另有两个仓内模块共享同一个 LocalBluetoothManager 单例(不碰 active,但共享事件线程,红队核验补全):base/settingsBaseUiVoiceAssistProvider.kt:416(查 PBAP 状态)、settingsPage/globalOnlyBluetoothRequestPermissionActivity.java:97

8. 自测清单

  • 能说出”档案卡 + 遥控器”各自对应哪些方法组?
  • mDevice 与 mActiveDevice 为什么有两个?谁在什么场景写?
  • 60s 看门狗的四种 profile 状态分别对应看门狗什么操作?
  • mProfiles 为空时 connect 链发生了什么?谁负责补连?
  • 小米黑名单存在哪个 Settings.Secure 键?f3dif 上为什么失效?
  • dcddif/xcddif/f3dif 各自的 MICAR-PORTING 标记数量?(答:10/33/0)
  • 蓝牙列表排序的五个优先级从高到低?

9. 交叉引用