bluetooth: Release transport when not available
[profile/ivi/pulseaudio-panda.git] / src / modules / bluetooth / module-bluetooth-device.c
index 8e3e48d..ad68cfa 100644 (file)
@@ -1229,10 +1229,21 @@ static pa_bt_audio_state_t parse_state_property_change(DBusMessage *m) {
     return state;
 }
 
+static pa_port_available_t audio_state_to_availability(pa_bt_audio_state_t state) {
+    if (state < PA_BT_AUDIO_STATE_CONNECTED)
+        return PA_PORT_AVAILABLE_NO;
+    else if (state >= PA_BT_AUDIO_STATE_PLAYING)
+        return PA_PORT_AVAILABLE_YES;
+    else
+        return PA_PORT_AVAILABLE_UNKNOWN;
+}
+
 /* Run from main thread */
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
     DBusError err;
     struct userdata *u;
+    bool acquire = FALSE;
+    bool release = FALSE;
 
     pa_assert(bus);
     pa_assert(m);
@@ -1299,6 +1310,85 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
                 }
                 break;
         }
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-output"));
+            pa_device_port_set_available(port, available);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "hfgw-input"));
+            pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HFGW);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-output"));
+            pa_device_port_set_available(port, available);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "hsp-input"));
+            pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_HSP);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-input"));
+            pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP_SOURCE);
+        }
+    } else if (dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged")) {
+        pa_bt_audio_state_t state = parse_state_property_change(m);
+
+        if (state != PA_BT_AUDIO_STATE_INVALID) {
+            pa_device_port *port;
+            pa_port_available_t available = audio_state_to_availability(state);
+
+            pa_assert_se(port = pa_hashmap_get(u->card->ports, "a2dp-output"));
+            pa_device_port_set_available(port, available);
+
+            acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == PROFILE_A2DP);
+        }
+    }
+
+    if (acquire)
+        if (bt_transport_acquire(u, FALSE) >= 0) {
+            if (u->source)
+                pa_source_suspend(u->source, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+
+            if (u->sink)
+                pa_sink_suspend(u->sink, FALSE, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
+        }
+
+    if (release && bt_transport_is_acquired(u)) {
+        /* FIXME: this release is racy, since the audio stream might have
+           been set up again in the meantime (but not processed yet by PA).
+           BlueZ should probably release the transport automatically, and
+           in that case we would just mark the transport as released */
+
+        /* Remote side closed the stream so we consider it PA_SUSPEND_USER */
+        if (u->source)
+            pa_source_suspend(u->source, TRUE, PA_SUSPEND_USER);
+
+        if (u->sink)
+            pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_USER);
     }
 
 fail:
@@ -2109,7 +2199,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     return 0;
 }
 
-static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_new_data, pa_card_profile *profile) {
+static void create_ports_for_profile(struct userdata *u, const pa_bluetooth_device *device, pa_card_new_data *card_new_data, pa_card_profile *profile) {
     pa_device_port *port;
     enum profile *d;
 
@@ -2122,6 +2212,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->audio_sink_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2131,6 +2222,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->audio_source_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2140,6 +2232,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->headset_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
 
             pa_assert_se(port = pa_device_port_new(u->core, "hsp-input", _("Bluetooth Telephony (HSP/HFP)"), 0));
@@ -2147,6 +2240,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->headset_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2156,6 +2250,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 1;
             port->is_input = 0;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->hfgw_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
 
             pa_assert_se(port = pa_device_port_new(u->core, "hfgw-input", _("Bluetooth Handsfree Gateway"), 0));
@@ -2163,6 +2258,7 @@ static void create_ports_for_profile(struct userdata *u, pa_card_new_data *card_
             port->is_output = 0;
             port->is_input = 1;
             port->priority = profile->priority * 100;
+            port->available = audio_state_to_availability(device->hfgw_state);
             pa_hashmap_put(port->profiles, profile->name, profile);
             break;
 
@@ -2226,7 +2322,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_A2DP;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2241,7 +2337,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_A2DP_SOURCE;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2257,7 +2353,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_HSP;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2272,7 +2368,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_HFGW;
-        create_ports_for_profile(u, &data, p);
+        create_ports_for_profile(u, device, &data, p);
 
         pa_hashmap_put(data.profiles, p->name, p);
     }
@@ -2471,6 +2567,9 @@ int pa__init(pa_module* m) {
                 mike,
                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
+                "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
                 NULL) < 0) {
 
         pa_xfree(speaker);