summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/modules/bluetooth/bluetooth-util.c109
-rw-r--r--src/modules/bluetooth/bluetooth-util.h25
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c13
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c14
4 files changed, 112 insertions, 49 deletions
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index dfebf663..771afff5 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -42,6 +42,22 @@ struct pa_bluetooth_discovery {
static void get_properties_reply(DBusPendingCall *pending, void *userdata);
static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func);
+static enum pa_bt_audio_state pa_bt_audio_state_from_string(const char* value) {
+ pa_assert(value);
+
+ if (pa_streq(value, "disconnected")) {
+ return PA_BT_AUDIO_STATE_DISCONNECTED;
+ } else if (pa_streq(value, "connecting")) {
+ return PA_BT_AUDIO_STATE_CONNECTING;
+ } else if (pa_streq(value, "connected")) {
+ return PA_BT_AUDIO_STATE_CONNECTED;
+ } else if (pa_streq(value, "playing")) {
+ return PA_BT_AUDIO_STATE_PLAYING;
+ }
+
+ return PA_BT_AUDIO_STATE_INVALID;
+}
+
static pa_bluetooth_uuid *uuid_new(const char *uuid) {
pa_bluetooth_uuid *u;
@@ -66,7 +82,7 @@ static pa_bluetooth_device* device_new(const char *path) {
d->dead = FALSE;
- d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
+ d->device_info_valid = 0;
d->name = NULL;
d->path = pa_xstrdup(path);
@@ -78,9 +94,9 @@ static pa_bluetooth_device* device_new(const char *path) {
d->class = -1;
d->trusted = -1;
- d->audio_sink_connected = -1;
-
- d->headset_connected = -1;
+ d->audio_state = PA_BT_AUDIO_STATE_INVALID;
+ d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID;
+ d->headset_state = PA_BT_AUDIO_STATE_INVALID;
return d;
}
@@ -102,25 +118,14 @@ static void device_free(pa_bluetooth_device *d) {
pa_xfree(d);
}
-static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
- pa_assert(d);
-
- return
- d->device_info_valid &&
- d->audio_sink_info_valid &&
- d->headset_info_valid;
-}
-
static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
pa_assert(d);
- pa_assert(d->device_info_valid);
- pa_assert(d->audio_sink_info_valid);
- pa_assert(d->headset_info_valid);
-
return
- d->device_info_valid > 0 &&
- (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
+ d->device_info_valid &&
+ (d->audio_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->headset_state != PA_BT_AUDIO_STATE_INVALID);
}
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
@@ -222,6 +227,11 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
node = uuid_new(value);
PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
+ /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ /* Vudentz said the interfaces are here when the UUIDs are announced */
if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
@@ -242,12 +252,12 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
return 0;
}
-static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
+static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessageIter *i) {
const char *key;
DBusMessageIter variant_i;
pa_assert(u);
- pa_assert(connected);
+ pa_assert(state);
pa_assert(i);
if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
@@ -269,17 +279,27 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM
dbus_message_iter_recurse(i, &variant_i);
-/* pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
+/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|Headset}.%s", key); */
switch (dbus_message_iter_get_arg_type(&variant_i)) {
+ case DBUS_TYPE_STRING: {
+
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "State"))
+ *state = pa_bt_audio_state_from_string(value);
+/* pa_log_debug("Value %s", value); */
+ }
+
case DBUS_TYPE_BOOLEAN: {
dbus_bool_t value;
dbus_message_iter_get_basic(&variant_i, &value);
- if (pa_streq(key, "Connected"))
- *connected = !!value;
+ /* if (pa_streq(key, "Connected")) */
+ /* *connected = !!value; */
/* pa_log_debug("Value %s", pa_yes_no(value)); */
@@ -294,9 +314,6 @@ static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_b
pa_assert(y);
pa_assert(d);
- if (!device_is_loaded(d))
- return;
-
if (!device_is_audio(d))
return;
@@ -326,10 +343,6 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
d->device_info_valid = valid;
- else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
- d->headset_info_valid = valid;
- else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
- d->audio_sink_info_valid = valid;
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
@@ -361,12 +374,16 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (parse_device_property(y, d, &dict_i) < 0)
goto finish;
+ } else if (dbus_message_has_interface(p->message, "org.bluez.Audio")) {
+ if (parse_audio_property(y, &d->audio_state, &dict_i) < 0)
+ goto finish;
+
} else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
- if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
+ if (parse_audio_property(y, &d->headset_state, &dict_i) < 0)
goto finish;
} else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
- if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
+ if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0)
goto finish;
}
}
@@ -572,7 +589,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
found_adapter(y, path);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
+ } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
@@ -590,12 +608,16 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (parse_device_property(y, d, &arg_i) < 0)
goto fail;
+ } else if (dbus_message_has_interface(m, "org.bluez.Audio")) {
+ if (parse_audio_property(y, &d->audio_state, &arg_i) < 0)
+ goto fail;
+
} else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
- if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
+ if (parse_audio_property(y, &d->headset_state, &arg_i) < 0)
goto fail;
} else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
- if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
+ if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0)
goto fail;
}
@@ -690,6 +712,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
"type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
pa_log("Failed to add D-Bus matches: %s", err.message);
@@ -746,6 +769,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
"type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
@@ -833,3 +857,16 @@ char *pa_bluetooth_cleanup_name(const char *name) {
return t;
}
+
+pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid) {
+ pa_assert(uuid);
+
+ while (uuids) {
+ if (strcasecmp(uuids->uuid, uuid) == 0)
+ return TRUE;
+
+ uuids = uuids->next;
+ }
+
+ return FALSE;
+}
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 57f11725..54114738 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -53,12 +53,20 @@ struct pa_bluetooth_uuid {
PA_LLIST_FIELDS(pa_bluetooth_uuid);
};
+/* This enum is shared among Audio, Headset, and AudioSink, although not all values are acceptable in all profiles */
+enum pa_bt_audio_state {
+ PA_BT_AUDIO_STATE_INVALID = -1,
+ PA_BT_AUDIO_STATE_DISCONNECTED,
+ PA_BT_AUDIO_STATE_CONNECTING,
+ PA_BT_AUDIO_STATE_CONNECTED,
+ PA_BT_AUDIO_STATE_PLAYING,
+ PA_BT_AUDIO_STATE_LAST
+};
+
struct pa_bluetooth_device {
pa_bool_t dead;
int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */
- int audio_sink_info_valid; /* ... same here ... */
- int headset_info_valid; /* ... and here */
/* Device information */
char *name;
@@ -71,11 +79,14 @@ struct pa_bluetooth_device {
int class;
int trusted;
- /* AudioSink information */
- int audio_sink_connected;
+ /* Audio state */
+ int audio_state;
- /* Headset information */
- int headset_connected;
+ /* AudioSink state */
+ int audio_sink_state;
+
+ /* Headset state */
+ int headset_state;
};
pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core);
@@ -93,4 +104,6 @@ const char* pa_bluetooth_get_form_factor(uint32_t class);
char *pa_bluetooth_cleanup_name(const char *name);
+pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid);
+
#endif
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 2c4f29c8..9fc1531e 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1736,11 +1736,11 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
d = PA_CARD_PROFILE_DATA(new_profile);
- if (u->device->headset_connected <= 0 && *d == PROFILE_HSP) {
+ if (u->device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) {
pa_log_warn("HSP is not connected, refused to switch profile");
return -1;
}
- else if (u->device->audio_sink_connected <= 0 && *d == PROFILE_A2DP) {
+ else if (u->device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) {
pa_log_warn("A2DP is not connected, refused to switch profile");
return -1;
}
@@ -1821,7 +1821,11 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- if (device->audio_sink_info_valid > 0) {
+ /* we base hsp/a2dp availability on UUIDs.
+ Ideally, it would be based on "Connected" state, but
+ we can't afford to wait for this information when
+ we are loaded with profile="hsp", for instance */
+ if (pa_bluetooth_uuid_has(device->uuids, A2DP_SINK_UUID)) {
p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile));
p->priority = 10;
p->n_sinks = 1;
@@ -1835,7 +1839,8 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
pa_hashmap_put(data.profiles, p->name, p);
}
- if (device->headset_info_valid > 0) {
+ if (pa_bluetooth_uuid_has(device->uuids, HSP_HS_UUID) ||
+ pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) {
p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
p->priority = 20;
p->n_sinks = 1;
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 49c7a800..6f3dba12 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -84,8 +84,7 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
mi = pa_hashmap_get(u->hashmap, d->path);
if (!d->dead &&
- d->device_connected > 0 &&
- (d->audio_sink_connected > 0 || d->headset_connected > 0)) {
+ d->device_connected > 0 && d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED) {
if (!mi) {
pa_module *m = NULL;
@@ -93,7 +92,16 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
/* Oh, awesome, a new device has shown up and been connected! */
- args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" profile=\"%s\"", d->address, d->path, d->headset_connected > 0 ? "hsp" : "a2dp");
+ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+#if 0
+ /* This is in case we have to use hsp immediately, without waiting for .Audio.State = Connected */
+ if (d->headset_state >= PA_BT_AUDIO_STATE_CONNECTED && somecondition) {
+ char *tmp;
+ tmp = pa_sprintf_malloc("%s profile=\"hsp\"", args);
+ pa_xfree(args);
+ args = tmp;
+ }
+#endif
#ifdef NOKIA
if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&