Я пытаюсь обновить наш код Bluetooth на компьютере, к которому наше приложение для Android подключается с помощью сокета rfcomm. До сих пор мы использовали параметр --compat для bluetoothd и некоторые функции SDP для регистрации нашей службы Bluetooth. Теперь я хочу использовать DBus API в Bluez 5, чтобы сделать его менее нестабильным и, возможно, заставить работать автоматическое подключение и автоматическое доверие после первого сопряжения.
Я могу зарегистрировать профиль с помощью метода RegisterProfile в org.bluez.ProfileManager1, затем я вижу свой собственный uuid в контроллере с помощью bluetoothctl, и приложение для Android тоже его видит. Затем мне нужен обратный вызов для запуска, когда устройство подключено к сокету, в который я могу писать. Я могу зарегистрироваться для получения сигнала PropertiesChanged на org.bluez.Device1, и он срабатывает как при подключении, так и при отключении, но не дает мне сокет. Я пытаюсь зарегистрировать объект с обратными вызовами в org.bluez.Profile1, но это просто не срабатывает. Я не совсем уверен, как следует обрабатывать UUID, поскольку мы будем использовать SPP, который имеет хорошо известный UUID, но я также хотел бы избежать шума и путаницы с другими устройствами, которые его используют, он должен общаться только с нашим приложением.
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;
}
Регистрация обратного вызова завершается без ошибок, вывод из этого:
register_profile called!
object path is good!
SPP profile registered
connection callback registered, id: 1;
Поэтому я не понимаю, почему моя функция connection_callback никогда не срабатывает. Я ценю все отзывы, я не разбираюсь ни в DBus, ни в Bluetooth.