如何使用 Bluez Profile1 DBus API 注册连接回调

Posted

技术标签:

【中文标题】如何使用 Bluez Profile1 DBus API 注册连接回调【英文标题】:How to register connection callbacks with Bluez Profile1 DBus API 【发布时间】:2019-12-21 23:52:44 【问题描述】:

我正在尝试更新我们的 android 应用程序使用 rfcomm 套接字连接到的计算机上的蓝牙代码。到目前为止,我们已经使用了 bluetoothd 的 --compat 选项和一些 SDP 功能来注册我们的蓝牙服务。现在我想在 Bluez 5 中使用 DBus API 来减少它的不稳定,并可能在第一次配对后使自动连接和自动信任工作。

我可以使用 org.bluez.ProfileManager1 中的 RegisterProfile 方法注册配置文件,然后使用 bluetoothctl 在控制器上看到我的自定义 uuid,Android 应用程序也会看到它。然后我需要一些回调来触发设备连接到有一个我可以写入的套接字。我可以在 org.bluez.Device1 注册 PropertiesChanged 信号,这会在连接和断开连接时触发,但它没有给我一个套接字。我尝试在 org.bluez.Profile1 注册一个带有回调的对象,但这不会触发。我也不太确定应该如何处理 UUID,因为我们将使用具有众所周知的 UUID 的 SPP,但我也想避免与使用它的其他设备产生噪音和混淆,它应该只与我们的应用程序对话。

static const gchar btp_introspection_xml[] =
    "<node>"
    "  <interface name='org.bluez.Profile1'>"
    "    <method name='Release' />"
    "    <method name='NewConnection'>"
    "      <arg type='o' name='device' direction='in' />"
    "      <arg type='h' name='fd' direction='in' />"
    "      <arg type='asv' name='fd_properties' direction='in' />"
    "    </method>"
    "    <method name='RequestDisconnection'>"
    "      <arg type='o' name='device' direction='in' />"
    "    </method>"
    "  </interface>"
    "</node>";


int register_profile(GDBusProxy *proxy)

    GVariant *profile;
    GVariantBuilder profile_builder;
    GError *error = NULL;

    printf("register_profile called!\n");

    g_variant_builder_init(&profile_builder, G_VARIANT_TYPE("(osasv)"));

    if (g_variant_is_object_path("/org/bluez/customprofile")) 
        printf("object path is good!\n");
    

    g_variant_builder_add (&profile_builder, "o",
            "/org/bluez/customprofile");

    g_variant_builder_add (&profile_builder, "s", SERIAL_PORT_PROFILE_UUID);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("asv"));


    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));   
    g_variant_builder_add (&profile_builder, "s", "Channel");
    g_variant_builder_add (&profile_builder, "v", g_variant_new_uint16(22));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "Service");
    g_variant_builder_add (&profile_builder, "v",
            g_variant_new_string(CUSTOM_UUID));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "Name");
    g_variant_builder_add (&profile_builder, "v",
            g_variant_new_string("Custom Serial Port"));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "Role");
    g_variant_builder_add (&profile_builder, "v",
                g_variant_new_string("server"));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "RequireAuthentication");
    g_variant_builder_add (&profile_builder, "v",
                g_variant_new_boolean(FALSE));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "RequireAuthorization");
    g_variant_builder_add (&profile_builder, "v",
                g_variant_new_boolean(FALSE));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("sv"));
    g_variant_builder_add (&profile_builder, "s", "AutoConnect");
    g_variant_builder_add (&profile_builder, "v",
                g_variant_new_boolean(TRUE));
    g_variant_builder_close(&profile_builder);

    g_variant_builder_close(&profile_builder);
    profile = g_variant_builder_end(&profile_builder);

    GVariant * ret = g_dbus_proxy_call_sync (proxy,
                "RegisterProfile",
                profile,
                G_DBUS_CALL_FLAGS_NONE,
                -1,
                NULL,
                &error);
    g_assert_no_error(error);
    if(ret != NULL && error==NULL)
        return 0;
     else 
        return 1;
    



static void connection_callback(GDBusConnection *conn, const char *sender,
        const char *path, const char *interface, const char *method, GVariant *params,
        GDBusMethodInvocation *invocation, void *userdata) 

    g_print("Called: %s.%s()", interface, method);
    (void)conn;
    (void)sender;
    (void)path;
    (void)params;

    if (strcmp(method, "NewConnection") == 0)
        g_print("connected");
    else if (strcmp(method, "RequestDisconnection") == 0)
        g_print("disconnected");
    else if (strcmp(method, "Release") == 0)
        g_print("released?");



static void bluez_signal_device_changed(GDBusConnection *conn,
                    const gchar *sender,
                    const gchar *path,
                    const gchar *interface,
                    const gchar *signal,
                    GVariant *params,
                    gpointer userdata)

    (void)conn;
    (void)sender;
    (void)path;
    (void)interface;

    GVariantIter *properties = NULL;
    GVariantIter *unknown = NULL;
    const char *iface;
    const char *key;
    GVariant *value = NULL;
    const gchar *signature = g_variant_get_type_string(params);

    if(strcmp(signature, "(sasvas)") != 0) 
        g_print("Invalid signature for %s: %s != %s", signal, signature, "(sasvas)");
        goto done;
    

    g_variant_get(params, "(&sasvas)", &iface, &properties, &unknown);
    while(g_variant_iter_next(properties, "&sv", &key, &value)) 
    g_print("key: %s | value: %s\n",key,value);
        if(!g_strcmp0(key, "Connected")) 
            if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) 
                g_print("Invalid argument type for %s: %s != %s", key,
                        g_variant_get_type_string(value), "b");
                goto done;
            
        gboolean status = g_variant_get_boolean(value);
            g_print("Device is \"%s\"\n", status ? "Connected" : "Disconnected");
        if (status) 
        //handle connection here instead?
         else 
        g_print("stopping writer");
        stop_bt_writer();
        destroy_json_handler();
        

        
    


done:
    if(properties != NULL)
        g_variant_iter_free(properties);
    if(value != NULL)
        g_variant_unref(value);



void init_server()

    GDBusProxy *proxy;
    GDBusConnection *conn;
    GError *error = NULL;
    OrgBluezProfile1 *interface;

    GMainLoop *loop = g_main_loop_new (NULL, FALSE);

    conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
    g_assert_no_error (error);

    proxy = g_dbus_proxy_new_sync (conn,
                G_DBUS_PROXY_FLAGS_NONE,
                NULL,/* GDBusInterfaceInfo */
                "org.bluez",/* name */
                "/org/bluez",/* object path */
                "org.bluez.ProfileManager1",/* interface */
                NULL,/* GCancellable */
                &error);
    g_assert_no_error (error);
    error=NULL;
    if (register_profile (proxy)) 
        printf("profile register failed\n");
        return;
    
    printf("SPP profile registered");

    //register device property callback (connect/disconnect)
    guint sub_id = g_dbus_connection_signal_subscribe(conn,
                    "org.bluez",
                    "org.freedesktop.DBus.Properties",
                    "PropertiesChanged",
                    NULL,
                    "org.bluez.Device1",
                    G_DBUS_SIGNAL_FLAGS_NONE,
                    bluez_signal_device_changed,
                    NULL,
                    NULL);

    static GDBusInterfaceVTable vtable = 
        .method_call = connection_callback,
    ;

    GDBusNodeInfo *introspection = g_dbus_node_info_new_for_xml(btp_introspection_xml, &error);
    GDBusInterfaceInfo *interface_info = g_dbus_node_info_lookup_interface(introspection, "org.bluez.Profile1");
    g_assert_no_error (error);
    error=NULL;
    guint regid = g_dbus_connection_register_object(conn,
                    "/org/bluez/customprofile",
                    interface_info,
                    &vtable,
                    NULL,
                    NULL,
                    &error);
    g_assert_no_error (error);
    printf("connection callback registered, id: %d;",regid);
    g_main_loop_run (loop);

    g_object_unref (proxy);
    g_object_unref (conn);

    return;

回调注册成功完成,输出如下:

register_profile called!
object path is good!
SPP profile registered
connection callback registered, id: 1;

所以我不明白为什么我的函数 connection_callback 永远不会触发。 感谢所有输入,我对 DBus 和蓝牙都没有经验

【问题讨论】:

【参考方案1】:

我的解决方案是将 bluez 从 5.37 升级到 5.50。 我也关注了How do I register a profile with bluez using dbus/gio?上的解决方案 并且升级后没有尝试过没有这个。如果不升级,仅此一项也无济于事

【讨论】:

以上是关于如何使用 Bluez Profile1 DBus API 注册连接回调的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 dbus/gio 向 bluez 注册个人资料?

如何使用 dbus-send 调用 org.bluez.Adapter1.StartDiscovery?

如何使用 dbus/bluez 在 python 中获取当前 MTU

使用 DBus 和 Bluez 将数据写入蓝牙设备

在 Bluez 中创建 dbus 接口

如何在 C++ 中使用 Bluez5 DBUS API 来配对和连接新设备?