You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中用sd-bus处理蓝牙配对密钥确认及外部配对请求

Ubuntu嵌入式设备蓝牙配对开发问题

我正在Ubuntu嵌入式设备上开发蓝牙应用,使用sd-bus库通过指定MAC地址配对蓝牙设备。现有代码可成功发起配对流程并弹出密钥确认对话框,代码如下:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <systemd/sd-bus.h>

#define _cleanup_(f) __attribute__((cleanup(f)))

#define DESTINATION   "org.bluez"
#define INTERFACE     "org.bluez.Device1"

static int log_error(int error, const char *message) {
    errno = -error;
    fprintf(stderr, "%s: %m\n", message);
    return error;
}

int bt_conn_control(char* bt_dev_mac) {
    _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    char *member_state = "Pair";
    char *path = NULL;
    int r;

    path = malloc(sizeof("/org/bluez/hci0/dev_00_00_00_00_00_00"));
    if (path == NULL) {
        perror("Memory allocation failed");
        return -1;
    }
    sprintf(path, "/org/bluez/hci0/dev_%c%c_%c%c_%c%c_%c%c_%c%c_%c%c",
            bt_dev_mac[0], bt_dev_mac[1], bt_dev_mac[3], bt_dev_mac[4], bt_dev_mac[6], bt_dev_mac[7],
            bt_dev_mac[9], bt_dev_mac[10], bt_dev_mac[12], bt_dev_mac[13], bt_dev_mac[15], bt_dev_mac[16]);

    r = sd_bus_open_system(&bus);
    if (r < 0) {  
        free(path);      
        return log_error(r, "Failed to acquire bus");
    }

    r = sd_bus_call_method(bus, DESTINATION, path, INTERFACE, member_state, &error, NULL, NULL, NULL);
    if (r < 0) {
        free(path);
        return log_error(r, "Method call failed");        
    }

    free(path);
    return 0;
}

int main() {
    bt_conn_control("3C:19:5E:F9:26:60");
    return 0;
}

我需要实现两个功能:

  • 在应用内以编程方式处理密钥确认,而非依赖Ubuntu系统UI;
  • 检测并在同一应用内管理外部配对请求。

一、编程处理密钥确认(绕过系统UI)

BlueZ的配对确认逻辑依赖org.bluez.Agent1接口,要在应用内处理密钥,需实现该接口并注册为BlueZ的配对代理。

1. 实现Agent1核心方法

根据配对类型(PIN码/密钥),实现RequestConfirmation、RequestPinCode等关键方法,示例如下:

#include <systemd/sd-bus.h>

#define AGENT_INTERFACE "org.bluez.Agent1"

// 处理密钥确认请求
static int agent_request_confirmation(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
    const char *device_path;
    uint32_t passkey;
    int r;

    r = sd_bus_message_read(m, "ou", &device_path, &passkey);
    if (r < 0) return r;

    // 自定义确认逻辑:可自动确认,或在应用内弹窗让用户交互
    printf("待确认密钥:%06u,设备路径:%s\n", passkey, device_path);

    // 自动确认(无需用户交互时使用)
    r = sd_bus_reply_method_return(m, "");
    return r;
}

// Agent接口的vtable定义
static const sd_bus_vtable agent_vtable[] = {
    SD_BUS_VTABLE_START(0),
    SD_BUS_METHOD("RequestConfirmation", "ou", "", agent_request_confirmation, SD_BUS_VTABLE_UNPRIVILEGED),
    // 根据需求添加其他方法:RequestPinCode、DisplayPasskey等
    SD_BUS_VTABLE_END
};

2. 注册Agent到BlueZ

在配对流程前,先将自定义Agent注册为BlueZ的默认代理:

int register_agent(sd_bus *bus) {
    int r;
    const char *agent_path = "/org/bluez/MyAgent";

    // 发布Agent对象到系统总线
    r = sd_bus_add_object_vtable(bus, NULL, agent_path, AGENT_INTERFACE, agent_vtable, NULL);
    if (r < 0) return log_error(r, "Failed to add agent vtable");

    // 注册Agent
    r = sd_bus_call_method(bus, DESTINATION, "/org/bluez", "org.bluez.AgentManager1",
                           "RegisterAgent", NULL, NULL, "os", agent_path, "DisplayOnly");
    if (r < 0) return log_error(r, "Failed to register agent");

    // 设置为默认Agent
    r = sd_bus_call_method(bus, DESTINATION, "/org/bluez", "org.bluez.AgentManager1",
                           "RequestDefaultAgent", NULL, NULL, "o", agent_path);
    if (r < 0) return log_error(r, "Failed to set default agent");

    return 0;
}

修改原bt_conn_control函数,先调用register_agent再发起配对,此时BlueZ会将密钥确认请求转发到你的应用Agent,而非系统UI。


二、检测并管理外部配对请求

外部设备发起的配对请求可通过监听org.bluez.Device1的PairingRequested信号捕获,再通过Agent接口处理。

1. 监听配对请求信号

static int handle_pairing_requested(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
    const char *device_path;
    int r;

    r = sd_bus_message_read(m, "o", &device_path);
    if (r < 0) return r;

    printf("收到外部配对请求,设备路径:%s\n", device_path);

    // 此处可获取设备MAC,根据业务逻辑决定接受/拒绝配对
    return 0;
}

int listen_pairing_requests(sd_bus *bus) {
    int r;

    // 订阅PairingRequested信号
    r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.bluez.Device1',member='PairingRequested'",
                         handle_pairing_requested, NULL);
    if (r < 0) return log_error(r, "Failed to add match for PairingRequested");

    return 0;
}

2. 获取设备MAC并处理配对

通过设备路径读取Address属性获取MAC,再调用对应方法处理:

// 根据设备路径获取MAC地址
char* get_device_mac(sd_bus *bus, const char *device_path) {
    _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    const char *mac;
    char *result = NULL;
    int r;

    r = sd_bus_get_property(bus, DESTINATION, device_path, INTERFACE, "Address", NULL, &reply);
    if (r < 0) {
        log_error(r, "Failed to get Address property");
        return NULL;
    }

    r = sd_bus_message_read(reply, "s", &mac);
    if (r < 0) {
        log_error(r, "Failed to read Address");
        return NULL;
    }

    result = strdup(mac);
    return result;
}

在handle_pairing_requested中调用上述函数获取MAC,若接受配对则调用Device1.Pair方法,拒绝则调用Device1.CancelPairing方法。


注意事项

  • 程序需蓝牙权限,可通过root运行或添加capability:sudo setcap cap_net_raw+ep ./your_program
  • 确保BlueZ服务正常运行:systemctl status bluetooth
  • 不同蓝牙设备类型(LE/传统蓝牙)可能需要补充实现更多Agent方法,按需添加。

内容的提问来源于stack exchange,提问作者its_me_mak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:55:12