Search code examples
cbluetoothglib

GLib for bluetooth client/server


I am trying to learn how to connect a server and client using bluetooth in C language and the GLib (to connect via D-Bus). I have successfully registered my service with it's own UUID with this code:

Register function:

int register_profile(GDBusProxy *proxy,
    gchar *service_path,
    gchar *service_name,
    gint service_channel,
    gchar *service_uuid)
{
    GVariant *profile;
    GVariantBuilder profile_builder;
    GError *error = NULL;

    g_variant_builder_init(&profile_builder, G_VARIANT_TYPE("(osa{sv})"));

    if (!g_variant_is_object_path(service_path))
    {
        return ERR_CUSTOM_PATH_INVALID;
    }

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

    g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("a{sv}"));
    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(service_channel));
    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(service_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(service_name));
    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 RESULT_OK;
    }
    return -1;
}

The code to receive connections events:

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

    g_print("Signal Device changed");
}

I initialize the code with this:

int register_service(char *service_path,
    char *service_name,
    int service_channel,
    char *service_uuid)
{
    GDBusProxy *proxy;
    GDBusConnection *conn, *pconn;
    GError *error = NULL;

    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,
        "org.bluez",
        "/org/bluez",
        "org.bluez.ProfileManager1",
        NULL,
        &error);
    g_assert_no_error(error);
    error = NULL;

    int result = register_profile(proxy, service_path, service_name, service_channel, service_uuid);
    if (result != RESULT_OK)
    {
        return result;
    }

    pconn = g_dbus_proxy_get_connection(proxy);
    guint sub_id = g_dbus_connection_signal_subscribe(pconn,
        "org.bluez",
        "org.freedesktop.DBus.Properties",
        "PropertiesChanged",
        NULL,
        "org.bluez.Device1",
        G_DBUS_SIGNAL_FLAGS_NONE,
        signal_device_changed,
        NULL,
        NULL);

    g_print("Registration successful with subscription id [%d]. Press [ENTER] to exit.", sub_id);
    char c;
    result = scanf("%c", &c);

    g_dbus_connection_signal_unsubscribe(pconn, sub_id);
    g_object_unref(pconn);
    g_object_unref(proxy);
    g_object_unref(conn);

    return RESULT_OK;
}

I use a python client application (which I tested with a python server and it worked on the device I used to test) to connect to the above server:

  • It can find the custom UUID
  • It seems to connect to it (it says it can find the UUID service and create a connection to it)
  • But I don't see my signal_device_changed function being called

I have tried using the conn rather than the proxy connection with same results.
Did I forget anything?

I used information from:


Solution

  • As advised by @ukBaz I needed to be in a main loop. I have modified the code as followed in the service registration:

    // Beginning is same
    serverUserData *userData = malloc(sizeof(serverUserData));
    userData->loop = g_main_loop_new(NULL, FALSE);
    
    guint sub_id = g_dbus_connection_signal_subscribe(pconn,
        "org.bluez",
        "org.freedesktop.DBus.Properties",
        "PropertiesChanged",
        NULL,
        "org.bluez.Device1",
        G_DBUS_SIGNAL_FLAGS_NONE,
        signal_device_changed,
        userData,
        NULL);
    
        if (sub_id > 0)
        {
            g_print("Registration successful with subscription id [%d].\n", sub_id);
        }
    
        g_main_loop_run(userData->loop);
    
        g_dbus_connection_signal_unsubscribe(pconn, sub_id);
        if (pconn != NULL)
            g_object_unref(pconn);
        if (proxy != NULL)
            g_object_unref(proxy);
        if (conn != NULL)
            g_object_unref(conn);
    

    And I quit the main loop when the device is disconnected.

    Next I need to figure out how to create an RFCOMM socket connection and listen on it. But if I face issue I'll do in another question.