# Deepin蓝牙回连机制详细说明

## 概述

本文档详细分析了Deepin系统中蓝牙自动回连（Auto-Reconnect）机制的完整实现逻辑，包括触发时机、设备筛选、排序策略、连接执行等各个环节。

## 1. 回连触发时机

蓝牙回连在以下5种系统事件发生时被触发：

### 1.1 系统初始化时
```go
// system/bluetooth/bluetooth.go 第321行
go b.tryConnectPairedDevices("")
```

### 1.2 适配器电源开启时
```go
// system/bluetooth/adapter.go 第167-175行
if a.Powered {
    go func() {
        // ... 停止扫描逻辑
        _bt.tryConnectPairedDevices(a.Path)  // 传入具体适配器路径
    }()
}
```

### 1.3 从睡眠唤醒时
```go
// system/bluetooth/bluetooth.go 第374-382行
func (b *SysBluetooth) handlePrepareForSleep(beforeSleep bool) {
    if beforeSleep {
        b.stopDiscovery()
        return
    }
    time.AfterFunc(time.Second*3, func() {
        b.tryConnectPairedDevices("")
    })
}
```

### 1.4 蓝牙服务重启时
```go
// system/bluetooth/bluetooth.go 第477-481行
if name == bluezDBusServiceName {
    if newOwner != "" {
        time.AfterFunc(1*time.Second, func() {
            b.tryConnectPairedDevices("")
        })
    }
}
```

### 1.5 用户代理注册时
```go
// system/bluetooth/bluetooth_ifc.go 第353行
go b.tryConnectPairedDevices("")
```

## 2. tryConnectPairedDevices函数详细流程

### 2.1 函数签名
```go
func (b *SysBluetooth) tryConnectPairedDevices(adapterPath dbus.ObjectPath)
```

### 2.2 完整执行流程

```mermaid
graph TD
    A[系统事件触发] --> B[权限检查]
    B --> C{autoPairEnable?}
    C -->|禁用| D[直接返回]
    C -->|启用| E[环境判断]
    E --> F{用户已登录?}
    F -->|是| G[连接超时: 20秒]
    F -->|否| H[连接超时: 2分钟]
    G --> I[获取配对设备]
    H --> I
    I --> J{adapterPath为空?}
    J -->|是| K[获取所有适配器设备]
    J -->|否| L[获取指定适配器设备]
    K --> M[设备过滤]
    L --> M
    M --> N{登录界面模式?}
    N -->|是| O[只保留输入设备]
    N -->|否| P[保留所有设备]
    O --> Q[设备智能排序]
    P --> Q
    Q --> R[创建autoDeviceInfo列表]
    R --> S[设置自动连接管理器]
    S --> T[启动回连流程]
```

### 2.3 关键代码逻辑

#### 权限检查（第902-909行）
```go
if b.canAutoPair != nil {
    if !b.canAutoPair() {
        logger.Info("can't auto pair because autoPairEnable key is unable")
        return
    }
}
```

#### 环境判断（第911-920行）
```go
inputOnly := true
defaultConnectDuration := 2 * time.Minute
if b.getActiveUserAgent() != nil {
    inputOnly = false
    defaultConnectDuration = 20 * time.Second
}
```

#### 设备过滤（第924-929行）
```go
if inputOnly {
    devices = filterOutDevices(devices, func(device *device) bool {
        return strings.HasPrefix(device.Icon, "input")
    })
}
```

#### 设备排序（第930-932行）
```go
logger.Debug("before soft devices:", devices)
b.config.softDevices(devices)  // 关键排序方法
logger.Debug("after soft devices:", devices)
```

## 3. 设备排序策略

### 3.1 softDevices排序逻辑

```go
// system/bluetooth/config.go 第259-279行
func (c *config) softDevices(devices []*device) {
    sort.SliceStable(devices, func(i, j int) bool {
        devI := devices[i]
        devJ := devices[j]
        cfgI := c.Devices[devI.getAddress()]
        cfgJ := c.Devices[devJ.getAddress()]
        var latestTimeI int64 = 0
        var latestTimeJ int64 = 0
        if cfgI != nil {
            latestTimeI = cfgI.LatestTime
        }
        if cfgJ != nil {
            latestTimeJ = cfgJ.LatestTime
        }
        return latestTimeI > latestTimeJ  // 最后连接时间越近，优先级越高
    })
}
```

### 3.2 设备类型优先级映射

```go
// system/bluetooth/config.go 第281-294行
var _iconPriorityMap = map[string]int{
    devIconInputMouse:      0, // 最高优先级 - 鼠标
    devIconInputKeyboard:   1, // 键盘
    devIconInputTablet:     2, // 平板
    devIconInputGaming:     2, // 游戏设备
    devIconAudioCard:       3, // 音频设备
    devIconPhone:           4, // 手机
    devIconComputer:        4, // 电脑
    devIconCameraPhoto:     5, // 相机
    devIconCameraVideo:     5, // 摄像机
    devIconModem:           6, // 调制解调器
    devIconNetworkWireless: 6, // 无线网络设备
    devIconPrinter:         7, // 打印机
}
```

### 3.3 排序优先级总结

1. **首要依据**：最后连接时间（最近连接的设备优先）
2. **次要依据**：设备类型优先级（输入设备 > 音频设备 > 其他设备）
3. **特殊情况**：无配置信息的设备排在最后

## 4. 自动连接管理器（AutoConnectManager）

### 4.1 核心数据结构

```go
// system/bluetooth/auto_connect_manager.go 第20-26行
type autoConnectManager struct {
    mu               sync.Mutex
    devices          map[dbus.ObjectPath]*autoDeviceInfo
    adapters         map[dbus.ObjectPath]*acmAdapterData
    connectCb        func(adapter, device dbus.ObjectPath, wId int) error
    startDiscoveryCb func(adapter dbus.ObjectPath)
}
```

### 4.2 设备信息结构

```go
type autoDeviceInfo struct {
    alias              string
    connectDurationMax time.Duration
    connectDuration    time.Duration
    adapter            dbus.ObjectPath
    device             dbus.ObjectPath
    wId                int
    priority           int // 优先级，越小越高
    count              int // 尝试连接次数
}
```

### 4.3 工作器启动流程

```mermaid
sequenceDiagram
    participant T as tryConnectPairedDevices
    participant A as AutoConnectManager
    participant W as Worker
    participant D as Device

    T->>A: addDevices(adapterPath, deviceInfos)
    A->>A: 设备排序和优先级设置
    A->>A: startWorkers(adapterPath)
    A->>W: newAutoConnectWorker()
    W->>W: start() goroutine
    loop 设备连接循环
        W->>A: getDevice(workerId)
        A->>W: 返回优先级最高的设备
        W->>W: connectCb(adapter, device, workerId)
        W->>D: autoConnectPairedDevice()
        D->>D: doConnect(false)
        alt 连接成功
            W->>A: removeDevice()
        else 连接失败
            W->>A: putDevice() 用于重试
        end
    end
```

## 5. 真正触发连接的地方

### 5.1 工作器核心循环

```go
// system/bluetooth/auto_connect_manager.go 第368-422行
func (w *autoConnectWorker) start() {
    go func() {
        d := w.m.getDevice(w.id)
        for {
            if d.device != "" {
                connectStart := time.Now()
                // 真正触发蓝牙回连的地方
                err := w.m.connectCb(d.adapter, d.device, w.id)
                connectDuration := time.Since(connectStart)
                
                if err != nil {
                    if err == errInvalidDevicePath {
                        w.m.removeDevice(d)
                    } else {
                        w.m.putDevice(d, connectDuration)  // 归还重试
                    }
                } else {
                    w.m.removeDevice(d)  // 连接成功，移除设备
                }
                
                d = w.m.getDevice(w.id)  // 获取下一个设备
            } else {
                break  // 无设备可连接，退出
            }
        }
    }()
}
```

### 5.2 连接回调函数

```go
// system/bluetooth/bluetooth.go 第135-148行
b.acm.connectCb = func(adapterPath, devicePath dbus.ObjectPath, wId int) error {
    adapter, err := b.getAdapter(adapterPath)
    if err != nil {
        return nil  // 适配器不存在，停止连接
    }
    if !adapter.Powered {
        return nil  // 适配器电源关闭，停止连接
    }
    adapter.autoConnectFinished = false
    return b.autoConnectPairedDevice(devicePath, adapterPath)
}
```

### 5.3 最终连接执行

```go
// system/bluetooth/bluetooth.go 第973-1005行
func (b *SysBluetooth) autoConnectPairedDevice(devPath dbus.ObjectPath, adapterPath dbus.ObjectPath) error {
    device, err := b.getDevice(devPath)
    if err != nil {
        return err
    }
    if device.connected {
        return nil  // 设备已连接，直接返回
    }
    
    // 设备类型检查和连接限制
    switch device.Icon {
    case devIconAudioCard, devIconInputKeyboard, devIconInputMouse, devIconInputTablet:
        connectedDevice := b.findFirstConnectedDeviceByIcon(device.Icon)
        if connectedDevice != nil {
            return nil  // 同类型设备已连接，不再连接
        }
    }
    
    logger.Debug("auto connect paired", device)
    return device.doConnect(false)  // 最终执行连接
}
```

## 6. 重连策略控制

### 6.1 maybeReconnectByDevice方法

```go
// system/bluetooth/device.go 第801-811行
func (d *device) maybeReconnectByDevice() bool {
    reconnectMode, err := d.getInputReconnectMode()
    if (reconnectMode == inputReconnectModeDevice || reconnectMode == inputReconnectModeAny) ||
        !d.isBREDRDevice() {
        return true  // 可能由设备端主动重连
    }
    return false
}
```

**使用场景**：在设备断开时设置Trusted为false，防止设备过快自动重连。

### 6.2 shouldReconnectByHost方法

```go
// system/bluetooth/device.go 第813-826行
func (d *device) shouldReconnectByHost() bool {
    reconnectMode, err := d.getInputReconnectMode()
    if reconnectMode == inputReconnectModeDevice {
        return false  // 由设备端重连，主机不主动重连
    }
    if (reconnectMode == inputReconnectModeHost || reconnectMode == inputReconnectModeAny) ||
        d.isBREDRDevice() {
        return true  // 由主机端主动重连
    }
    return false
}
```

**使用场景**：决定是否在蓝牙回连过程中对该设备进行自动连接。

### 6.3 重连模式定义

```go
const (
    inputReconnectModeNone   = "none"    // 不重连
    inputReconnectModeHost   = "host"    // 由主机重连
    inputReconnectModeDevice = "device"  // 由设备重连
    inputReconnectModeAny    = "any"     // 任意端重连
)
```

## 7. 错误处理和重试机制

### 7.1 连接失败处理

```go
// 连接失败后的处理逻辑
if err != nil {
    logger.Debugf("failed to auto connect %v: %v", device, err)
    device.setConnectPhase(connectPhaseConnectFailedSleep)
    time.Sleep(3 * time.Second)  // 失败后等待3秒
    device.setConnectPhase(connectPhaseNone)
}
```

### 7.2 重试次数限制

```go
// 失败3次后降低优先级
if info.count >= 3 {
    info.priority = priorityLowest  // priorityLowest = 99
}
```

### 7.3 超时控制

每个设备有最大连接时长限制：
- 用户已登录：20秒
- 登录界面：2分钟

## 8. 设计特点总结

### 8.1 智能排序策略
- **时间优先**：最近使用的设备优先连接
- **类型优先**：输入设备优先于音频设备
- **用户习惯**：根据用户使用习惯动态调整优先级

### 8.2 并发控制
- 每个适配器最多1个工作器，避免连接冲突
- 有序的设备连接，确保重要设备优先

### 8.3 错误恢复
- 连接失败后的智能重试机制
- 失败次数限制，避免资源浪费
- 超时控制，防止长时间阻塞

### 8.4 资源管理
- 工作器自动回收机制
- 设备连接状态实时更新
- 配置信息持久化存储

## 9. 完整流程图

```mermaid
graph TB
    A[系统事件] --> B{权限检查}
    B -->|禁用| C[直接返回]
    B -->|启用| D[环境判断]
    D --> E[获取配对设备]
    E --> F[设备过滤]
    F --> G[智能排序]
    G --> H[创建设备列表]
    H --> I[设置自动连接管理器]
    I --> J[启动工作器]
    J --> K[工作器循环]
    K --> L{获取设备}
    L -->|有设备| M[执行连接]
    L -->|无设备| N[退出工作器]
    M --> O{连接结果}
    O -->|成功| P[移除设备]
    O -->|失败| Q{失败原因}
    Q -->|设备不存在| R[移除设备]
    Q -->|其他错误| S[归还设备重试]
    P --> L
    R --> L
    S --> L
    N --> T[启动设备扫描]
```

这个蓝牙回连机制通过精心的设计和多层次的策略，确保了用户设备能够智能、稳定地自动重新连接，提供了良好的用户体验。