diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 2feea66c0..26147f96e 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -915,6 +915,11 @@ static void update_last_used(struct a2dp_channel *chan, struct a2dp_sep *lsep,
 					avdtp_get_seid(rsep));
 }
 
+static void destroy_setup(void *data)
+{
+	setup_unref(data);
+}
+
 static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 				struct avdtp_stream *stream, uint8_t *err,
 				void *user_data)
@@ -927,10 +932,21 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
 	else
 		DBG("Source %p: Open_Ind", sep);
 
+	if (avdtp_stream_get_user_data(stream)) {
+		warn("setup already exists");
+		return FALSE;
+	}
+
 	setup = a2dp_setup_get(session);
 	if (!setup)
 		return FALSE;
 
+	/*
+	 * Use avdtp_stream's user_data to keep the reference ownership of the
+	 * setup object. This ensures that this reference will be unref-ed
+	 * exactly once.
+	 */
+	avdtp_stream_set_user_data(stream, setup, destroy_setup);
 	setup->stream = stream;
 
 	if (!err && setup->chan)
@@ -1285,14 +1301,13 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			void *user_data)
 {
 	struct a2dp_sep *a2dp_sep = user_data;
-	struct a2dp_setup *setup;
+	struct a2dp_setup *setup = avdtp_stream_get_user_data(stream);
 
 	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
 		DBG("Sink %p: Abort_Cfm", sep);
 	else
 		DBG("Source %p: Abort_Cfm", sep);
 
-	setup = find_setup_by_session(session);
 	if (!setup)
 		return;
 
@@ -1302,6 +1317,7 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	}
 
 	setup_unref(setup);
+	avdtp_stream_set_user_data(stream, NULL, NULL);
 }
 
 static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -2216,11 +2232,12 @@ fail:
 
 static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
 {
-	struct a2dp_setup *setup = user_data;
+	struct avdtp_stream *stream = user_data;
+	struct a2dp_setup *setup = avdtp_stream_get_user_data(stream);
 	uint16_t omtu, imtu;
 
-	if (!g_slist_find(setups, setup)) {
-		warn("bt_io_accept: setup %p no longer valid", setup);
+	if (!setup) {
+		warn("transport_cb: setup does not exist");
 		g_io_channel_shutdown(io, TRUE, NULL);
 		return;
 	}
@@ -2238,8 +2255,7 @@ static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
 		goto drop;
 	}
 
-	if (!avdtp_stream_set_transport(setup->stream,
-					g_io_channel_unix_get_fd(io),
+	if (!avdtp_stream_set_transport(stream, g_io_channel_unix_get_fd(io),
 					imtu, omtu))
 		goto drop;
 
@@ -2249,6 +2265,7 @@ static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
 	setup->io = NULL;
 
 	setup_unref(setup);
+	avdtp_stream_set_user_data(stream, NULL, NULL);
 
 	return;
 
@@ -2297,7 +2314,8 @@ static void confirm_cb(GIOChannel *io, gpointer data)
 			goto drop;
 		}
 
-		if (!bt_io_accept(io, transport_cb, setup, NULL, &err)) {
+		if (!bt_io_accept(io, transport_cb, setup->stream, NULL,
+				  &err)) {
 			error("bt_io_accept: %s", err->message);
 			g_error_free(err);
 			goto drop;
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 782268c08..76d06f47c 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -366,6 +366,8 @@ struct avdtp_stream {
 	GSList *caps;
 	GSList *callbacks;
 	struct avdtp_service_capability *codec;
+	void *user_data;
+	avdtp_stream_user_data_destroy_t user_data_destroy;
 	guint io_id;		/* Transport GSource ID */
 	guint timer;		/* Waiting for other side to close or open
 				 * the transport channel */
@@ -727,6 +729,9 @@ static void stream_free(void *data)
 	g_slist_free_full(stream->callbacks, g_free);
 	g_slist_free_full(stream->caps, g_free);
 
+	if (stream->user_data && stream->user_data_destroy)
+		stream->user_data_destroy(stream->user_data);
+
 	g_free(stream);
 }
 
@@ -3147,6 +3152,18 @@ struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
 	return NULL;
 }
 
+void avdtp_stream_set_user_data(struct avdtp_stream *stream, void *data,
+				avdtp_stream_user_data_destroy_t destroy)
+{
+	stream->user_data = data;
+	stream->user_data_destroy = destroy;
+}
+
+void *avdtp_stream_get_user_data(struct avdtp_stream *stream)
+{
+	return stream->user_data;
+}
+
 gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
 						size_t imtu, size_t omtu)
 {
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 011fea89e..247e9df75 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -133,6 +133,8 @@ typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
 						struct avdtp_stream *stream,
 						struct avdtp_error *err);
 
+typedef void (*avdtp_stream_user_data_destroy_t)(void *data);
+
 /* Callbacks for when a reply is received to a command that we sent */
 struct avdtp_sep_cfm {
 	void (*set_configuration) (struct avdtp *session,
@@ -260,6 +262,9 @@ gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
 					GSList *caps);
 struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
 						struct avdtp_stream *stream);
+void avdtp_stream_set_user_data(struct avdtp_stream *stream, void *data,
+				avdtp_stream_user_data_destroy_t destroy);
+void *avdtp_stream_get_user_data(struct avdtp_stream *stream);
 
 unsigned int avdtp_add_state_cb(struct btd_device *dev,
 				avdtp_session_state_cb cb, void *user_data);
