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

Bluez DBUS api NewConnection method gives wrong file descriptor

我正在尝试使用 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, "(oha{sv})", 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='a{sv}' 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()
            }
        }
    }

所以我找到了问题所在。接口中名为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 回调参数。这个returns列表,那么实际的文件描述符可以使用

得到
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, "(oha{sv})", 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()
    }
}