21 · settingslib 架构与事件流
核心文件:settingslib “七姐妹”——
LocalBluetoothManager/LocalBluetoothAdapter/BluetoothEventManager/CachedBluetoothDeviceManager/CachedBluetoothDevice/LocalBluetoothProfileManager/BluetoothCallback三 flavor:base/settingsLibAndroid/src/{dcddif,xcddif,f3dif}/java/com/android/settingslib/bluetooth/。dcddif=骁龙8295 / xcddif=玄戒A14 / f3dif=玄戒A17主线(dev_a17_0610 编译 f3dif)。 适用范围:理解本仓蓝牙事件流的前提。
0. 为什么先读这篇
前序文档反复出现 MiBluetoothUtils.getLocalBtManager()、getEventManager().registerCallback(this)、CachedBluetoothDevice.connect()——它们都落到 AOSP settingslib 的”四件套”上:连接车机蓝牙 UI 与系统 BluetoothAdapter 的中间层。本仓在 AOSP 基础上做了 MIUI/MICAR 定制。
🚨 关键认知(对抗审查+主控核实):“settingslib 是 AOSP 原样拷贝”错误;但 MICAR 定制只存在于 dcddif/xcddif。f3dif(A17 主线)= 纯上游 AOSP,
CachedBluetoothDevice.java中 0 个 MICAR-PORTING 标记(xcddif 33 个、dcddif 10 个),黑名单/半关/try-catch/timeout 翻倍等定制全部回归。改 A17 蓝牙逻辑时不能假设这些定制点存在。下文凡 MICAR 定制均标注仅 dcddif/xcddif。
1. LocalBluetoothManager:单例与聚合
业务层入口 MiBluetoothUtils.getLocalBtManager()(:73-89) → LocalBluetoothManager.getInstance。构造(:109-121 dcddif/xcddif;f3dif :110-129 因 EventManager 6 参)聚合四件套并立即 updateLocalProfiles() + readPairedDevices()——构造完即广播已注册、profile handler 已挂、已配对设备已入 Cache。
f3dif
getInstance(:60-62) 调LocalBluetoothAdapter.getInstance(context, user)(双参),xcddif/dcddif 调无参getInstance();f3dif 还新增mCachedFocussedDisplayId(:55) 多屏 focus。
classDiagram class LocalBluetoothManager { +getInstance(ctx,cb)$ +getCachedDeviceManager() +getEventManager() +getProfileManager() -WeakReference mForegroundActivity } class LocalBluetoothAdapter { +startScanning(force) } class BluetoothEventManager { -Map Handler mHandlerMap -CopyOnWriteArrayList BluetoothCallback mCallbacks +registerCallback(cb) } class CachedBluetoothDeviceManager { +findDevice()/addDevice() } class LocalBluetoothProfileManager { +updateLocalProfiles() } class CachedBluetoothDevice { +connect()/connectProfile() +onBondingStateChanged()/onUuidChanged() } LocalBluetoothManager o--> LocalBluetoothAdapter LocalBluetoothManager o--> BluetoothEventManager LocalBluetoothManager o--> CachedBluetoothDeviceManager LocalBluetoothManager o--> LocalBluetoothProfileManager BluetoothEventManager --> CachedBluetoothDeviceManager : dispatch CachedBluetoothDeviceManager *-- CachedBluetoothDevice
mForegroundActivity弱持有(:44)。Fragment 进前台调setForegroundActivity(BluetoothMainEntrySettingsFragment:217),让 settingslib 知道”有 BT UI 在前台”。
2. BluetoothEventManager:广播总线
构造器里 addHandler(action, handler) 注册 action→Handler 路由表,action 累加到 IntentFilter 后 registerReceiver。三 flavor 广播数量不同:
| flavor | addHandler 范围 | 数量 | 特征 |
|---|---|---|---|
| xcddif | :108-181 | 29 | 全集:含 CSIS/TWSP/LeAudio/Codec/Broadcast/VCP/Screen |
| dcddif | :91-127 | 18 | 少 11 项(无 Le/Auracast/CSIS/VCP/Screen) |
| f3dif | :103-143 | 20 | 新增 ACTION_AUTO_ON_STATE_CHANGED(:143,A17独有);缺 CSIS/TWSP/Codec/Broadcast/VCP/Screen |
主要 action→Handler 路由(xcddif):
| Action | Handler | 分发目标 |
|---|---|---|
ACTION_STATE_CHANGED | AdapterStateChangedHandler | onBluetoothStateChanged + MIUI 半关(仅xcddif :180-181) |
ACTION_FOUND | DeviceFoundHandler | mDeviceManager.addDevice + dispatchDeviceAdded |
ACTION_BOND_STATE_CHANGED | BondStateChangedHandler | onDeviceBondStateChanged + cachedDevice.onBondingStateChanged |
ACTION_UUID | UuidChangedHandler | cachedDevice.onUuidChanged()(触发自动连接旁路) |
ACTION_ACL_CONNECTED/DISCONNECTED | AclStateChangedHandler | onAclConnectionStateChanged |
ACTION_ACTIVE_DEVICE_CHANGED | ActiveDeviceChangedHandler | onActiveDeviceChanged |
各 profile ACTION_CONNECTION_STATE_CHANGED | StateChangedHandler(LBPM) | onProfileStateChanged |
MICAR 定制 try/catch 包裹广播分发仅 xcddif 有(:386-392),dcddif/f3dif 无——单个 handler 抛异常在 f3dif/dcddif 会让后续广播丢失。RECEIVER_EXPORTED:xcddif(:231,235)+f3dif(:179,183) 有,dcddif 无(A14 兼容老 API)。
事件总线模型
flowchart LR subgraph 系统["系统 BluetoothAdapter"] A1[广播 ACTION_*] end subgraph EM["BluetoothEventManager(主线程)"] BR[onReceive] --> HM[mHandlerMap→Handler] HM --> D2[findDevice/addDevice] HM --> CD[CachedBluetoothDevice.onXxx] HM --> D1[dispatch 遍历 mCallbacks] end subgraph UI["BluetoothCallback 实现"] F1[MainEntryFragment] F2[BondedController] F3[UnbondedController] end A1 --> BR D1 --> F1 D1 --> F2 D1 --> F3
关键性质:Handler 表 1:1;mCallbacks 是 CopyOnWriteArrayList;mReceiverHandler 绑主线程——所有 BluetoothCallback 在主线程触发(上层 Controller 大量 postOnBackgroundThread + mUIHandler.post 切换线程的根本原因)。
3. CachedBluetoothDevice:聚合体
每个实例 = 一个远端设备 + bond 状态 + profile 列表 + 名称/电量/活跃态。三 flavor 的 connect 链与黑名单差异巨大:
3.1 connect 链(三栏)
| 步骤 | dcddif | xcddif | f3dif |
|---|---|---|---|
onBondingStateChanged 签名 | (int):839 | (int):1050 | (int,int):1147 |
| 黑名单早退 | isCloseAutoConnectDevice:853 | :1079 | 不存在 |
→ connect()(public) | :360 | :429 | :503 |
mConnectAttempted 赋值 | :365 | :434 | onAclStateChanged:1227 |
→ connectDevice()/connectAllEnabledProfiles | :387(adapter:402) | :521(mDevice:544) | :596(mDevice:611) |
dcddif 走
mActiveAdapter.connectAllEnabledProfiles(mDevice);xcddif/f3dif 走mDevice.connect()(BluetoothDevice 隐藏 API),让框架按 priority 一次性连所有 profile。
3.2 黑名单 isCloseAutoConnectDevice(仅 dcddif/xcddif)
// xcddif CachedBluetoothDevice.java:1032-1046 + 早退 :1079
private boolean isCloseAutoConnectDevice(BluetoothDevice device) {
String devicesStr = Settings.Secure.getStringForUser(..., BLUETOOTH_CLOSE_AUTO_CONNECT, ...);
return device.getAddress().equals(devicesStr); // 单槽位 MAC 等值匹配
}
// onBondingStateChanged 内: if (isCloseAutoConnectDevice(mDevice)) return; // :1079🚨 f3dif 完全没有此方法(grep 0 命中)。
onBondingStateChanged(:1147) 直接if(mDevice.isBondingInitiatedLocally()) connect()(:1192)。disableAutoConnect写 Settings.Secure 在所有 flavor 都执行——A17 主线”写得到、没人读”,CarPlay 配对后仍自动连 HFP/A2DP。
3.3 onUuidChanged 旁路(三 flavor 一致:都不查黑名单)
onUuidChanged() 在 mConnectAttempted + timeout 窗口内直接 connectDevice()/connectAllEnabledProfiles(),不查黑名单。timeout:xcddif/dcddif 翻倍(X+X);f3dif 不翻倍但用大常量——MAX_UUID 35000(vs 5000)、MAX_HOGP 60000(vs 30000)、MAX_HEARING_AIDS 45000(vs 15000)、MAX_LEAUDIO 60000。
3.4 onBondingStateChanged 自动连接(仅车机主动发起)
MICAR 定制:只有 isBondingInitiatedLocally=true(车机主动发起配对)才自动 connect;对端发起的不连。xcddif(:1075 缓存局部变量→:1090)、dcddif(:851 合并在 if);f3dif(:1192 直接调,无黑名单)。
4. 自动连接端到端时序
sequenceDiagram participant FW as 蓝牙框架 participant CDC as CachedBluetoothDevice participant CB as BluetoothCallback FW->>CDC: ACTION_BOND_STATE_CHANGED(BOND_BONDED) Note over CDC: xcddif onBondingStateChanged:1050<br/>dcddif :839<br/>f3dif :1147(双参) alt dcddif/xcddif 且命中黑名单(CarPlay) CDC->>CDC: isCloseAutoConnectDevice 早退<br/>(xcddif:1079/dcddif:853) else f3dif(无黑名单,直接走) CDC->>CDC: 无黑名单检查 end alt isBondingInitiatedLocally(车机发起) CDC->>CDC: connect() alt mProfiles 空(UUID未到) FW->>CDC: ACTION_UUID→onUuidChanged CDC->>CDC: timeout窗口内 connectDevice 旁路(不查黑名单) else mProfiles 非空 CDC->>FW: mDevice.connect()(xcddif:544/f3dif:611)<br/>或 connectAllEnabledProfiles(dcddif:402) end FW-->>CB: onProfileConnectionStateChanged → UI刷新 else 对端发起 Note over CDC: settingslib 不主动 connect end
5. LocalBluetoothProfileManager:profile 注册
updateLocalProfiles() 按 adapter.getSupportedProfiles() 动态创建 profile 实例,每个通过 addProfile→addProfileHandler 加到 mProfileIntentFilter(独立 mProfileBroadcastReceiver)。
profile 实例化顺序 ≠ 连接顺序。
connect()走框架内部 priority(通常 HFP→A2DP→PBAP),settingslib 不感知。connectProfile(LocalBluetoothProfile)(:568) 才是 settingslib 主动连单个 profile 的入口(详情页 PBAP 开关走这条)。
6. BluetoothCallback:UI 层接入
| flavor | 方法数 | 范围 | 特征 |
|---|---|---|---|
| xcddif | 15 | :54-238 | 含 Codec/CSIS/Broadcast 回调 |
| dcddif | 10 | :36-141 | 少 5 个 |
| f3dif | 11 | :52-174 | 新增 onAutoOnStateChanged(:174,A17独有);缺 Codec/CSIS/Broadcast |
主要回调:onBluetoothStateChanged/onScanningStateChanged/onDeviceAdded/onDeviceBondStateChanged/onProfileConnectionStateChanged/onAclConnectionStateChanged/onActiveDeviceChanged 等。
注册时机:BluetoothMainEntrySettingsFragment onStart:216/onStop:223;各 Controller onStartInternal:115/onStopInternal:123。必须配对,漏注销致泄漏。
7. 双蓝牙上限
DOUBLE_BLUETOOTH_DEVICE_LIMIT=2(ConnectionConstants.kt:19)。5 处调用点在应用层(micarConnectionSettings,非 settingslib),详见 32-双蓝牙上限与Picker。判定口径 = 已连 HFP/A2DP 设备数(ConnectionUtils.getConnectedHfpA2dpDevices),非手机类不计入。
8. 三 flavor 对照总表(核心速查)
| 项目 | dcddif | xcddif | f3dif(A17主线) |
|---|---|---|---|
| BluetoothEventManager 广播数 | 18 | 29 | 20(含AUTO_ON) |
| BluetoothCallback 方法数 | 10 | 15 | 11(含onAutoOn) |
| onBondingStateChanged | 单参:839 | 单参:1050 | 双参:1147 |
| connect 链终点 | adapter:402 | mDevice:544 | mDevice:611 |
| isCloseAutoConnectDevice 黑名单 | :821/:853 | :1032/:1079 | 不存在 |
| MICAR-PORTING 标记数(CBD) | 10 | 33 | 0(纯AOSP) |
| timeout 翻倍 | X+X:793 | X+X:1003 | 单X大常量:1113 |
| MAX_UUID_DELAY | 5000 | 5000 | 35000 |
| try/catch 广播保护 | 无 | 有:386 | 无 |
| 半关 ScreenChangedHandler | 无 | 有:180 | 无 |
| LeAudio/HapClient/CsipSet | 无 | 有 | 有 |
| RECEIVER_EXPORTED | 无 | 有 | 有 |
9. 对抗自查清单
- 行号必标 flavor:上表三栏对照,勿用单 flavor 行号套全 flavor。
isAutoConnectable不在 connect 链:是LocalBluetoothProfile接口方法(:36),各 profile 实现,框架层使用,settingslib 不主动调(grep 三 flavor 0 调用)。- “settingslib 是 AOSP 拷贝”证伪,但 f3dif 接近原版:MICAR 定制(黑名单/半关/try-catch/timeout翻倍)只在 dcddif/xcddif;f3dif 是纯上游 AOSP,改 A17 逻辑勿假设定制存在。
getInstance非空保证:getLocalBtManager()用!!,车机必须有蓝牙适配器。- 回调主线程:所有 dispatch 在主线程,Controller 的线程切换是为此。
- f3dif 黑名单缺失是 A17 真实风险:CarPlay 配对后自动连蓝牙抢占通道(见 31 H6)。