简体   繁体   中英

How to register connection callbacks with Bluez Profile1 DBus API

I'm trying to update our bluetooth code on a computer that our android app connects to using rfcomm socket. Until now we have used the --compat option for bluetoothd and some SDP functions to register our bluetooth service. Now I want to use the DBus API in Bluez 5 to make it less flaky and possibly make auto-connect and auto-trust work after first pairing.

I can register a profile using the RegisterProfile method at org.bluez.ProfileManager1, then I see my custom uuid at the contoller using bluetoothctl, and the android app see it too. Then I need some callback to trigger when the device is connected to have a socket I can write to. I can register for the PropertiesChanged signal at org.bluez.Device1, and this does trigger at both connect and disconnect, but it doesn't give me a socket. I try to register an object with callbacks at org.bluez.Profile1, but this just won't trigger. I'm not quite sure how UUIDs should be handled either since we will use SPP which has a well known UUID, but I would also like to avoid noise and confusion with other devices that uses this, it should only talk to our app.

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='a{sv}' 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("(osa{sv})"));

    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("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(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, "(sa{sv}as)") != 0) {
        g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
        goto done;
    }

    g_variant_get(params, "(&sa{sv}as)", &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;
}

The callback registration completes without error, the output from this is:

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

So I dont understand why my function connection_callback never triggers. I appreciate all input, I am not experienced in neither DBus nor bluetooth

My solution was to upgrade bluez from 5.37 to 5.50. I also followed the solution on How do I register a profile with bluez using dbus/gio? and have not tried without this after upgrading. That alone did not help without upgrading though

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM