Bluez DBUS api NewConnection 方法给出了错误的文件描述符

Posted

技术标签:

【中文标题】Bluez DBUS api NewConnection 方法给出了错误的文件描述符【英文标题】:Bluez DBUS api NewConnection method gives wrong file descriptor 【发布时间】:2021-05-09 17:10:46 【问题描述】:

我正在尝试使用 DBUS bluez api 实现 RFCOMM 配置文件。我已经实现了org.bluez.Profile1 接口并调用了NewConnection 方法,但是文件描述符参数错误。每次调用该方法时,文件描述符都是 0。当我尝试写入它时,我将 errno 设置为 Bad file descriptor

这是 method_call 回调的代码:

val methodCall = staticCFunction<
        CPointer<GDBusConnection>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<GVariant>?,
        CPointer<GDBusMethodInvocation>?,
        gpointer?,
        Unit >
dbus, sender, objectPath, interfaceName, methodName, parameters, invocation, userData ->
    memScoped 
        val caller = allocPointerTo<gcharVar>()
        val fd = alloc<gint32Var>()
        val properties = allocPointerTo<GVariantIter>()
        fd.value = -1

        g_variant_get(parameters, "(ohasv)", caller.ptr, fd, properties.ptr)
        """
            Method call sender: $sender?.toKString() 
            objectPath: $objectPath?.toKString()
            interfaceName: $interfaceName?.toKString()
            methodName: $methodName?.toKString()
            caller: $caller.value?.toKString()
            fd: $fd.value
        """.debug()

        val text = "text".cstr
        "Written: $write(fd.value, text, text.size.convert()) bytes".debug()
        strerror(errno)?.toKString()?.error()
    

产生这个输出:

11:43:43    [DEBUG] 
            Method call sender: :1.3 
            objectPath: /org/bluez/myprofile
            interfaceName: org.bluez.Profile1
            methodName: NewConnection
            caller: /org/bluez/hci0/dev_......
            fd: 0
            
11:43:43    [DEBUG] Written: -1 bytes
11:43:43    [ERROR] Bad file descriptor

我正在使用此代码注册实现接口的对象:

fun registerBluetoothProfileObject() 
        val interfaceDesc =
            """
                <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>
            """

        val vTable = cValue<GDBusInterfaceVTable> 
            method_call = Bluetooth.methodCall //This points to the method_call callback
            set_property = Bluetooth.setProperty
            get_property = Bluetooth.getProperty
        
        memScoped 
            val error = allocPointerTo<GError>()
            val nodeInfo = g_dbus_node_info_new_for_xml(interfaceDesc, error.ptr)
            error.value?.let 
                "Error creating node from xml $it.pointed.message?.toKString()".error()
            

            val interfaceInfo = g_dbus_node_info_lookup_interface(nodeInfo, "org.bluez.Profile1")

            g_dbus_connection_register_object(
                dbus,
                Bluetooth.PROFILE_OBJECT_PATH,
                interfaceInfo,
                vTable.ptr,
                null,
                null,
                error.ptr
            )

            error.value?.let 
                "Error registering sdp profile object: $it.pointed.message?.toKString()".error()
            
        
    

【问题讨论】:

【参考方案1】:

所以我发现了问题所在。接口中名为fd的参数实际上不是文件描述符。它是文件描述符字段的索引。所以我得到的0 值是文件描述符数组中文件描述符的索引,可以使用类似这样的方法获得

val fdList = g_dbus_message_get_unix_fd_list(g_dbus_method_invocation_get_message(invocation))

传递给g_dbus_method_invocation_get_message方法的invocation参数来自callMethod回调参数。这将返回列表,然后可以使用获取实际的文件描述符

val fd = g_unix_fd_list_get(fdList, fdIndex.value, null)

所以回调函数是唯一需要改变的东西。现在看起来像这样

val methodCall = staticCFunction<
        CPointer<GDBusConnection>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<gcharVar>?,
        CPointer<GVariant>?,
        CPointer<GDBusMethodInvocation>?,
        gpointer?,
        Unit >
dbus, sender, objectPath, interfaceName, methodName, parameters, invocation, userData ->
    memScoped 
        val caller = allocPointerTo<gcharVar>()
        val fdIndex = alloc<gint32Var>()
        val properties = allocPointerTo<GVariantIter>()

        g_variant_get(parameters, "(ohasv)", caller.ptr, fdIndex, properties.ptr)
        """
            Method call sender: $sender?.toKString() 
            objectPath: $objectPath?.toKString()
            interfaceName: $interfaceName?.toKString()
            methodName: $methodName?.toKString()
            caller: $caller.value?.toKString()
            fd: $fdIndex.value
        """.debug()

        val fdList = g_dbus_message_get_unix_fd_list(g_dbus_method_invocation_get_message(invocation))

        val fd = g_unix_fd_list_get(fdList, fdIndex.value, null)

        val text = "text".cstr
        "Written: $write(fd, text, text.size.convert()) bytes".debug()
    

【讨论】:

以上是关于Bluez DBUS api NewConnection 方法给出了错误的文件描述符的主要内容,如果未能解决你的问题,请参考以下文章

BlueZ DBUS API - GATT 接口对 BLE 设备不可用

Bluez DBUS api NewConnection 方法给出了错误的文件描述符

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

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

使用 Dbus API for bluez 5.31 写入特征值在内核 4.1 中失败

在 Bluez 中创建 dbus 接口