Search code examples
cglibdbusbluezkotlin-native

Bluez DBUS api NewConnection method gives wrong file descriptor


I am trying to implement RFCOMM profile using the DBUS bluez api. I have implemented the org.bluez.Profile1 interface and the NewConnection method gets called, but file descriptor parameter is wrong. Every time the method gets called the file descriptor is 0. And when i try to write to it i get errno set to Bad file descriptor.

This is code for the method_call callback:

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()
    }
}

Which produces this output:

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

I am registering the object implementing the interface with this code:

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()
            }
        }
    }

Solution

  • So i found out where the problem is. The parameter called fd in the interface is actually not file descriptor. It is index to a field of file descriptors. So the 0 value i am getting is index of the file descriptor in array of file descriptors which can be obtained using something like this

    val fdList = g_dbus_message_get_unix_fd_list(g_dbus_method_invocation_get_message(invocation))
    

    the invocation parameter passed to the g_dbus_method_invocation_get_message method is from the callMethod callback parameter. This returns the list, then the actual file descriptor can be obtained using

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

    So the callback function was the only thing that needed change. Now it looks like this

    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()
        }
    }