#include <glib.h>
#include <gio/gio.h>
#include <stdio.h>
#include <string.h>
#define BLUEZ_SERVICE "org.bluez"
#define AGENT_INTERFACE "org.bluez.Agent1"
#define AGENT_MANAGER_INTERFACE "org.bluez.AgentManager1"
#define DEVICE_INTERFACE "org.bluez.Device1"
#define AGENT_PATH "/org/bluez/example/agent"
static GMainLoop *main_loop = NULL;
static GDBusConnection *connection = NULL;
static gchar *device_address = NULL;
static gchar *adapter = "hci0"; // 移到全局以解决初始化问题
/* 兼容低版本GLib,实现字符串替换函数 */
static gchar *str_replace(const gchar *string, const gchar *search, const gchar *replace) {
gchar *result = g_strdup(string);
gchar *temp;
gint search_len = strlen(search);
gint replace_len = strlen(replace);
while ((temp = strstr(result, search)) != NULL) {
// 计算新字符串长度
gint result_len = strlen(result);
gint new_len = result_len - search_len + replace_len + 1;
// 分配新内存
gchar *new_result = g_malloc(new_len);
// 复制前面部分
gint prefix_len = temp - result;
strncpy(new_result, result, prefix_len);
new_result[prefix_len] = '\0';
// 拼接替换字符串和剩余部分
strcat(new_result, replace);
strcat(new_result, temp + search_len);
// 更新结果并释放旧内存
g_free(result);
result = new_result;
}
return result;
}
/* 配对代理方法实现 */
static void agent_release(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data) {
g_print("Agent released\n");
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void agent_request_pin_code(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data) {
gchar *device_path;
g_variant_get(parameters, "(o)", &device_path);
g_print("Requesting PIN code for device: %s\n", device_path);
g_print("Enter PIN code: ");
char pin[17];
fgets(pin, sizeof(pin), stdin);
pin[strcspn(pin, "\n")] = '\0'; // 移除换行符
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(s)", pin));
g_free(device_path);
}
static void agent_request_confirmation(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data) {
gchar *device_path;
guint32 passkey;
g_variant_get(parameters, "(ou)", &device_path, &passkey);
g_print("Confirm pairing with device %s (passkey: %06d)? (y/n) ",
device_path, passkey);
char response[10];
fgets(response, sizeof(response), stdin);
if (response[0] == 'y' || response[0] == 'Y') {
g_dbus_method_invocation_return_value(invocation, NULL);
} else {
GError *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CANCELLED,
"User rejected pairing");
g_dbus_method_invocation_return_error(invocation,
error->domain,
error->code,
"%s", error->message);
g_error_free(error);
}
g_free(device_path);
}
static void agent_cancel(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data) {
g_print("Pairing cancelled\n");
g_dbus_method_invocation_return_value(invocation, NULL);
}
/* 方法调用处理函数 */
static void agent_method_call(GDBusConnection *conn,
const gchar *sender,
const gchar *obj_path,
const gchar *iface_name,
const gchar *method_name,
GVariant *params,
GDBusMethodInvocation *invoc,
gpointer udata) {
if (g_strcmp0(method_name, "Release") == 0) {
agent_release(conn, sender, obj_path, iface_name, method_name,
params, invoc, udata);
} else if (g_strcmp0(method_name, "RequestPinCode") == 0) {
agent_request_pin_code(conn, sender, obj_path, iface_name, method_name,
params, invoc, udata);
} else if (g_strcmp0(method_name, "RequestConfirmation") == 0) {
agent_request_confirmation(conn, sender, obj_path, iface_name, method_name,
params, invoc, udata);
} else if (g_strcmp0(method_name, "Cancel") == 0) {
agent_cancel(conn, sender, obj_path, iface_name, method_name,
params, invoc, udata);
} else {
g_dbus_method_invocation_return_error(invoc,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method %s", method_name);
}
}
/* 定义Agent接口的方法表 */
static const GDBusInterfaceVTable agent_vtable = {
.method_call = agent_method_call,
.get_property = NULL,
.set_property = NULL
};
/* 注册Agent到BlueZ */
static gboolean register_agent(GDBusConnection *conn) {
GError *error = NULL;
GVariant *result;
result = g_dbus_connection_call_sync(conn,
BLUEZ_SERVICE,
"/org/bluez",
AGENT_MANAGER_INTERFACE,
"RegisterAgent",
g_variant_new("(os)", AGENT_PATH, "DisplayYesNo"),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error != NULL) {
g_printerr("Failed to register agent: %s\n", error->message);
g_error_free(error);
return FALSE;
}
g_variant_unref(result);
/* 设置为默认Agent */
result = g_dbus_connection_call_sync(conn,
BLUEZ_SERVICE,
"/org/bluez",
AGENT_MANAGER_INTERFACE,
"RequestDefaultAgent",
g_variant_new("(o)", AGENT_PATH),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error != NULL) {
g_printerr("Failed to set default agent: %s\n", error->message);
g_error_free(error);
return FALSE;
}
g_variant_unref(result);
g_print("Agent registered successfully\n");
return TRUE;
}
/* 开始与指定设备配对 */
static gboolean pair_device(GDBusConnection *conn, const gchar *adapter_path) {
gchar *device_path;
GError *error = NULL;
GVariant *result;
gchar *address_copy, *replaced_address;
if (!device_address) {
g_printerr("No device address specified\n");
return FALSE;
}
/* 使用自定义的字符串替换函数 */
address_copy = g_strdup(device_address);
replaced_address = str_replace(address_copy, ":", "_");
device_path = g_strdup_printf("%s/dev_%s", adapter_path, replaced_address);
g_print("Pairing with device: %s\n", device_path);
/* 调用配对方法 */
result = g_dbus_connection_call_sync(conn,
BLUEZ_SERVICE,
device_path,
DEVICE_INTERFACE,
"Pair",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
60000, // 60秒超时
NULL,
&error);
if (error != NULL) {
g_printerr("Pairing failed: %s\n", error->message);
g_error_free(error);
g_free(device_path);
g_free(replaced_address);
g_free(address_copy);
return FALSE;
}
g_print("Pairing successful\n");
/* 绑定设备(保存配对信息) */
g_dbus_connection_call_sync(conn,
BLUEZ_SERVICE,
device_path,
DEVICE_INTERFACE,
"Connect",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
60000,
NULL,
&error);
if (error != NULL) {
g_printerr("Connection failed: %s\n", error->message);
g_error_free(error);
g_free(device_path);
g_free(replaced_address);
g_free(address_copy);
g_variant_unref(result);
return FALSE;
}
g_print("Device connected successfully\n");
g_free(device_path);
g_free(replaced_address);
g_free(address_copy);
g_variant_unref(result);
return TRUE;
}
/* 设备属性变化信号处理 */
static void device_property_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
gpointer user_data) {
GVariantIter *properties;
const gchar *iface;
gchar *key;
GVariant *value;
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, NULL);
if (g_strcmp0(iface, DEVICE_INTERFACE) != 0)
return;
while (g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if (g_strcmp0(key, "Paired") == 0) {
gboolean paired = g_variant_get_boolean(value);
g_print("Device paired status: %s\n", paired ? "Paired" : "Not paired");
if (paired) {
g_print("Pairing completed successfully\n");
g_main_loop_quit(main_loop);
}
} else if (g_strcmp0(key, "Connected") == 0) {
gboolean connected = g_variant_get_boolean(value);
g_print("Device connection status: %s\n", connected ? "Connected" : "Disconnected");
}
g_variant_unref(value);
}
g_variant_iter_free(properties);
}
static void usage(const gchar *progname) {
g_print("Usage: %s [OPTIONS] <device_address>\n", progname);
g_print("Options:\n");
g_print(" -a, --adapter=ADAPTER Use specified adapter (default: hci0)\n");
g_print(" -h, --help Show this help message\n");
}
int main(int argc, char *argv[]) {
GError *error = NULL;
guint agent_reg_id;
guint signal_id;
gchar *adapter_path;
/* 解析命令行参数 - 修复初始化问题 */
static const GOptionEntry options[] = {
{"adapter", 'a', 0, G_OPTION_ARG_STRING, &adapter, "Use specified adapter", "ADAPTER"},
{"help", 'h', 0, G_OPTION_ARG_NONE, NULL, "Show help", NULL},
{NULL}
};
GOptionContext *context = g_option_context_new("- BlueZ pairing example");
g_option_context_add_main_entries(context, options, NULL);
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_printerr("Option parsing failed: %s\n", error->message);
g_error_free(error);
return 1;
}
g_option_context_free(context);
if (argc < 2) {
usage(argv[0]);
return 1;
}
device_address = argv[1];
adapter_path = g_strdup_printf("/org/bluez/%s", adapter);
/* 条件调用g_type_init,兼容不同版本GLib */
#if !GLIB_CHECK_VERSION(2,36,0)
g_type_init();
#endif
/* 获取系统总线连接 */
connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!connection) {
g_printerr("Failed to connect to system bus: %s\n", error->message);
g_error_free(error);
return 1;
}
/* 注册Agent接口 */
agent_reg_id = g_dbus_connection_register_object(connection,
AGENT_PATH,
g_dbus_node_info_new_for_xml(
"<node>"
" <interface name='org.bluez.Agent1'>"
" <method name='Release'/>"
" <method name='RequestPinCode'>"
" <arg type='o' name='device' direction='in'/>"
" <arg type='s' name='pincode' direction='out'/>"
" </method>"
" <method name='RequestConfirmation'>"
" <arg type='o' name='device' direction='in'/>"
" <arg type='u' name='passkey' direction='in'/>"
" </method>"
" <method name='Cancel'/>"
" </interface>"
"</node>",
NULL)->interfaces[0],
&agent_vtable,
NULL, NULL, &error);
if (error != NULL) {
g_printerr("Failed to register agent object: %s\n", error->message);
g_error_free(error);
return 1;
}
/* 注册设备属性变化信号监听 */
signal_id = g_dbus_connection_signal_subscribe(connection,
BLUEZ_SERVICE,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
DEVICE_INTERFACE,
G_DBUS_SIGNAL_FLAGS_NONE,
device_property_changed,
NULL,
NULL);
/* 注册Agent到BlueZ */
if (!register_agent(connection)) {
g_dbus_connection_unregister_object(connection, agent_reg_id);
g_object_unref(connection);
return 1;
}
/* 开始配对过程 */
if (!pair_device(connection, adapter_path)) {
g_dbus_connection_signal_unsubscribe(connection, signal_id);
g_dbus_connection_unregister_object(connection, agent_reg_id);
g_object_unref(connection);
return 1;
}
/* 启动主循环 */
main_loop = g_main_loop_new(NULL, FALSE);
g_print("Waiting for pairing to complete...\n");
g_main_loop_run(main_loop);
/* 清理资源 */
g_main_loop_unref(main_loop);
g_dbus_connection_signal_unsubscribe(connection, signal_id);
g_dbus_connection_unregister_object(connection, agent_reg_id);
g_object_unref(connection);
g_free(adapter_path);
return 0;
}
最新发布