OSDN Git Service

audio: Don't create an avctp session on avrcp disconnect
authorAlex Deymo <deymo@chromium.org>
Fri, 3 May 2013 09:39:56 +0000 (12:39 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 3 May 2013 10:25:54 +0000 (13:25 +0300)
If a paired and connected audio device is disconnected, the
avrcp_disconnect() could create a new avctp session that will keep
a reference to the corresponding btd_device, preventing it to be
removed as explained below. This fix prevents avrcp_disconnect()
to create a new and disconnected avctp session when it doesn't
exists.

Calling org.bluez.Device1.Disconnect on an audio device like the
"Monster ClarityHD" will cause first a a2dp_sink_disconnect() call,
and then a sink_disconnect() call.
This will change the state of the existing avdtp session to
AVCTP_STATE_DISCONNECTED triggering a series of callback calls.
Among those, the avdtp_set_state() function will call the registered
avdtp_callbacks, including avdtp_state_callback() which in turns
updates the disconnected state using sink_set_state(). This function
will call the registered sink_callbacks, including device_sink_cb().

By this point, the device_sink_cb() will attempt a avrcp_disconnect()
over a session that was already disconnected before by the device's
diconnect_cb(). This new avrcp_disconnect() causes the avctp_get() to
create a new avctp session that holds a reference to the disconnecting
btd_device.

Steps to reproduce using bluetoothctl:
1. Pair and Connect a Monter ClarityHD audio device.
2. Play some music on it.
3. Disconnect the device.
4. Remove the device.
The "remove" command succeeds, but bluetoothd does not sends a removal
signal ([DEL] message) for that device.

profiles/audio/device.c

index 11e1455..5423645 100644 (file)
@@ -172,7 +172,7 @@ static void disconnect_cb(struct btd_device *btd_dev, gboolean removal,
 
        device_remove_control_timer(dev);
 
-       if (dev->control)
+       if (dev->control && priv->avctp_state != AVCTP_STATE_DISCONNECTED)
                avrcp_disconnect(dev);
 
        if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
@@ -243,7 +243,8 @@ static void device_sink_cb(struct audio_device *dev,
        case SINK_STATE_DISCONNECTED:
                if (dev->control) {
                        device_remove_control_timer(dev);
-                       avrcp_disconnect(dev);
+                       if (priv->avctp_state != AVCTP_STATE_DISCONNECTED)
+                               avrcp_disconnect(dev);
                }
 
                device_set_state(dev, AUDIO_STATE_DISCONNECTED);