diff options
-rw-r--r-- | src/modules/bluetooth/bluez5-util.c | 201 | ||||
-rw-r--r-- | src/modules/bluetooth/bluez5-util.h | 4 |
2 files changed, 205 insertions, 0 deletions
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index 5b6b372f9..55743b784 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -43,12 +43,17 @@ #define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1" #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1" #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1" +#define BLUEZ_PROFILE_MANAGER_INTERFACE BLUEZ_SERVICE ".ProfileManager1" +#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1" #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported" #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" +#define HSP_AG_PROFILE "/Profile/HSPAGProfile" +#define HFP_AG_PROFILE "/Profile/HFPAGProfile" + #define ENDPOINT_INTROSPECT_XML \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ "<node>" \ @@ -74,6 +79,30 @@ " </interface>" \ "</node>" +#define PROFILE_INTROSPECT_XML \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "<node>" \ + " <interface name=\"" BLUEZ_PROFILE_INTERFACE "\">" \ + " <method name=\"Release\">" \ + " </method>" \ + " <method name=\"Cancel\">" \ + " </method>" \ + " <method name=\"RequestDisconnection\">" \ + " <arg name=\"device\" direction=\"in\" type=\"o\"/>" \ + " </method>" \ + " <method name=\"NewConnection\">" \ + " <arg name=\"device\" direction=\"in\" type=\"o\"/>" \ + " <arg name=\"fd\" direction=\"in\" type=\"h\"/>" \ + " <arg name=\"opts\" direction=\"in\" type=\"a{sv}\"/>" \ + " </method>" \ + " </interface>" \ + " <interface name=\"org.freedesktop.DBus.Introspectable\">" \ + " <method name=\"Introspect\">" \ + " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \ + " </method>" \ + " </interface>" \ + "</node>" + struct pa_bluetooth_discovery { PA_REFCNT_DECLARE; @@ -767,6 +796,56 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint)); } +static void register_profile_reply(DBusPendingCall *pending, void *userdata) { + DBusMessage *r; + pa_dbus_pending *p; + pa_bluetooth_discovery *y; + char *profile; + + pa_assert(pending); + pa_assert_se(p = userdata); + pa_assert_se(y = p->context_data); + pa_assert_se(profile = p->call_data); + pa_assert_se(r = dbus_pending_call_steal_reply(pending)); + + if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) { + pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", profile); + goto finish; + } + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r), + pa_dbus_get_error_message(r)); + goto finish; + } + +finish: + dbus_message_unref(r); + + PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); + pa_dbus_pending_free(p); + + pa_xfree(profile); +} + +static void register_profile(pa_bluetooth_discovery *y, const char *profile, const char *uuid) { + DBusMessage *m; + DBusMessageIter i, d; + + pa_log_debug("Registering Profile %s", profile); + + pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile")); + + dbus_message_iter_init_append(m, &i); + dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &profile); + dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid); + dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d); + dbus_message_iter_close_container(&i, &d); + + send_and_add_to_pending(y, m, register_profile_reply, pa_xstrdup(profile)); +} + static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) { DBusMessageIter element_i; const char *path; @@ -1109,6 +1188,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { return "a2dp_sink"; case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: return "a2dp_source"; + case PA_BLUETOOTH_PROFILE_HSP_AG: + return "hsp_ag"; + case PA_BLUETOOTH_PROFILE_HFP_AG: + return "hfp_ag"; case PA_BLUETOOTH_PROFILE_OFF: return "off"; } @@ -1529,6 +1612,120 @@ static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t prof } } +static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) { + pa_bluetooth_discovery *y = userdata; + int i, size; + DBusMessage *r; + DBusError err; + int fd; + const char *path; + DBusMessageIter arg_i; + + if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) { + pa_log_error("Invalid signature found in NewConnection"); + goto fail; + } + + pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH); + dbus_message_iter_get_basic(&arg_i, &path); + + pa_assert_se(dbus_message_iter_next(&arg_i)); + + pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD); + dbus_message_iter_get_basic(&arg_i, &fd); + + pa_log_debug("dbus: NewConnection path=%s, fd=%d", path, fd); + + pa_assert_se(r = dbus_message_new_method_return(m)); + + return r; + +fail: + pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to handle new connection")); + return r; +} + +static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void *userdata) { + struct pa_bluetooth_discovery *y = userdata; + DBusMessage *r = NULL; + const char *path, *interface, *member; + + pa_assert(y); + + path = dbus_message_get_path(m); + interface = dbus_message_get_interface(m); + member = dbus_message_get_member(m); + + pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); + + if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HFP_AG_PROFILE)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { + const char *xml = PROFILE_INTROSPECT_XML; + + pa_assert_se(r = dbus_message_new_method_return(m)); + pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); + + } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) { + } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Cancel")) { + } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) { + } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection")) + r = profile_new_connection(c, m, userdata); + else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (r) { + pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL)); + dbus_message_unref(r); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void profile_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) { + static const DBusObjectPathVTable vtable_profile = { + .message_function = profile_handler, + }; + const char *object_name; + const char *uuid; + + pa_assert(y); + + switch(profile) { + case PA_BLUETOOTH_PROFILE_HSP_AG: + object_name = HSP_AG_PROFILE; + uuid = PA_BLUETOOTH_UUID_HSP_AG; + break; + case PA_BLUETOOTH_PROFILE_HFP_AG: + object_name = HFP_AG_PROFILE; + uuid = PA_BLUETOOTH_UUID_HFP_AG; + break; + default: + pa_assert_not_reached(); + break; + } + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), + object_name, &vtable_profile, y)); + register_profile (y, object_name, uuid); +} + +static void profile_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) { + pa_assert(y); + + switch(profile) { + case PA_BLUETOOTH_PROFILE_HSP_AG: + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HSP_AG_PROFILE); + break; + case PA_BLUETOOTH_PROFILE_HFP_AG: + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_PROFILE); + break; + default: + pa_assert_not_reached(); + break; + } +} + pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { pa_bluetooth_discovery *y; DBusError err; @@ -1586,6 +1783,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK); endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE); + profile_init(y, PA_BLUETOOTH_PROFILE_HSP_AG); + profile_init(y, PA_BLUETOOTH_PROFILE_HFP_AG); get_managed_objects(y); @@ -1650,6 +1849,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK); endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE); + profile_done(y, PA_BLUETOOTH_PROFILE_HSP_AG); + profile_done(y, PA_BLUETOOTH_PROFILE_HFP_AG); pa_dbus_connection_unref(y->connection); } diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index 63bae35ee..43200fc8e 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -26,6 +26,8 @@ #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" +#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" +#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" typedef struct pa_bluetooth_transport pa_bluetooth_transport; typedef struct pa_bluetooth_device pa_bluetooth_device; @@ -41,6 +43,8 @@ typedef enum pa_bluetooth_hook { typedef enum profile { PA_BLUETOOTH_PROFILE_A2DP_SINK, PA_BLUETOOTH_PROFILE_A2DP_SOURCE, + PA_BLUETOOTH_PROFILE_HSP_AG, + PA_BLUETOOTH_PROFILE_HFP_AG, PA_BLUETOOTH_PROFILE_OFF } pa_bluetooth_profile_t; #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF |