From: Bharat Panda Date: Tue, 10 Nov 2015 05:37:38 +0000 (+0530) Subject: audio: Fix possible crash when removing device X-Git-Tag: accepted/tizen/mobile/20151202.230855~10 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=27d1dee399d94f0769048599b2e5e45474930158;p=platform%2Fupstream%2Fbluez.git audio: Fix possible crash when removing device Currently it is not possible to cancel avdtp_discover procedure leading to crashe if the device is removed while avdtp_discover is pending since its callback is still reachable. git repo link: http://git.kernel.org/cgit/bluetooth/bluez.git/commit/?id=33b447daaa3adfc04dfbc123538360d998e2d7d7 Change-Id: I607f818bbe0fe1287b421548fb41d1047bb88210 --- diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index 464faeb..14de80c 100644 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -82,6 +82,7 @@ struct a2dp_sep { struct a2dp_setup_cb { struct a2dp_setup *setup; + a2dp_discover_cb_t discover_cb; a2dp_select_cb_t select_cb; a2dp_config_cb_t config_cb; a2dp_stream_cb_t resume_cb; @@ -98,6 +99,7 @@ struct a2dp_setup { struct avdtp_stream *stream; struct avdtp_error *err; avdtp_set_configuration_cb setconf_cb; + GSList *seps; GSList *caps; gboolean reconfigure; gboolean start; @@ -302,6 +304,23 @@ static void finalize_select(struct a2dp_setup *s) } } +static void finalize_discover(struct a2dp_setup *s) +{ + GSList *l; + + for (l = s->cb; l != NULL; ) { + struct a2dp_setup_cb *cb = l->data; + + l = l->next; + + if (!cb->discover_cb) + continue; + + cb->discover_cb(s->session, s->seps, s->err, cb->user_data); + setup_cb_free(cb); + } +} + static struct a2dp_setup *find_setup_by_session(struct avdtp *session) { GSList *l; @@ -1838,6 +1857,40 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type, return a2dp_find_sep(session, l, NULL); } +static void discover_cb(struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data) +{ + struct a2dp_setup *setup = user_data; + + DBG("err %p", err); + + setup->seps = seps; + setup->err = err; + + finalize_discover(setup); +} + +unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb, + void *user_data) +{ + struct a2dp_setup *setup; + struct a2dp_setup_cb *cb_data; + + setup = a2dp_setup_get(session); + if (!setup) + return 0; + + cb_data = setup_cb_new(setup); + cb_data->discover_cb = cb; + cb_data->user_data = user_data; + + if (avdtp_discover(session, discover_cb, setup) == 0) + return cb_data->id; + + setup_cb_free(cb_data); + return 0; +} + unsigned int a2dp_select_capabilities(struct avdtp *session, uint8_t type, const char *sender, a2dp_select_cb_t cb, @@ -2131,8 +2184,8 @@ gboolean a2dp_cancel(unsigned int id) if (!setup->cb) { DBG("aborting setup %p", setup); - avdtp_abort(setup->session, setup->stream); - return TRUE; + if (!avdtp_abort(setup->session, setup->stream)) + return TRUE; } setup_unref(setup); diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h index 544eea1..50c1eeb 100644 --- a/profiles/audio/a2dp.h +++ b/profiles/audio/a2dp.h @@ -52,6 +52,9 @@ struct a2dp_endpoint { void *user_data); }; +typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps, + struct avdtp_error *err, + void *user_data); typedef void (*a2dp_select_cb_t) (struct avdtp *session, struct a2dp_sep *sep, GSList *caps, void *user_data); @@ -70,6 +73,8 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type, int *err); void a2dp_remove_sep(struct a2dp_sep *sep); +unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb, + void *user_data); unsigned int a2dp_select_capabilities(struct avdtp *session, uint8_t type, const char *sender, a2dp_select_cb_t cb, diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index b9db7f3..aba2d03 100644 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -3703,6 +3703,13 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) struct seid_req req; int ret; + if (!stream && session->discover) { + /* Don't call cb since it being aborted */ + session->discover->cb = NULL; + finalize_discovery(session, -ECANCELED); + return -EALREADY; + } + if (!g_slist_find(session->streams, stream)) return -EINVAL; diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c index 750b710..cdd89b4 100644 --- a/profiles/audio/sink.c +++ b/profiles/audio/sink.c @@ -287,7 +287,9 @@ gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session) if (!sink->session) return FALSE; - if (avdtp_discover(sink->session, discovery_complete, sink) < 0) + sink->connect_id = a2dp_discover(sink->session, discovery_complete, + sink); + if (sink->connect_id == 0) return FALSE; return TRUE; diff --git a/profiles/audio/source.c b/profiles/audio/source.c index 43c20c2..11e4cff 100644 --- a/profiles/audio/source.c +++ b/profiles/audio/source.c @@ -273,7 +273,9 @@ gboolean source_setup_stream(struct btd_service *service, if (!source->session) return FALSE; - if (avdtp_discover(source->session, discovery_complete, source) < 0) + source->connect_id = a2dp_discover(source->session, discovery_complete, + source); + if (source->connect_id == 0) return FALSE; return TRUE;