From 484fba70190a56d1ae74b03cd527db049f80acd3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 16 Jan 2014 14:27:53 +0200 Subject: [PATCH] android/A2DP: Fix selecting invalid bitpool range for SBC For SBC bitpool range may not strictly match with presets so it needs to be adjusted to fit within remote capabilities otherwise the remote device may reject the configuration. --- android/a2dp.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/android/a2dp.c b/android/a2dp.c index e3e425ce8..7c98c5f1c 100644 --- a/android/a2dp.c +++ b/android/a2dp.c @@ -229,6 +229,24 @@ static int sbc_check_config(void *caps, uint8_t caps_len, void *conf, return -EINVAL; } + if (config->max_bitpool < cap->min_bitpool) { + error("SBC: Invalid maximun bitpool (%u < %u)", + config->max_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->min_bitpool > cap->max_bitpool) { + error("SBC: Invalid minimun bitpool (%u > %u)", + config->min_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->max_bitpool > cap->max_bitpool) + return -ERANGE; + + if (config->min_bitpool < cap->min_bitpool) + return -ERANGE; + return 0; } @@ -246,22 +264,61 @@ static int check_capabilities(struct a2dp_preset *preset, } } +static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len, + void *conf, uint8_t conf_len) +{ + struct a2dp_preset *p; + a2dp_sbc_t *cap, *config; + + cap = caps; + config = conf; + + config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool); + config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool); + + p = g_new0(struct a2dp_preset, 1); + p->len = conf_len; + p->data = g_memdup(conf, p->len); + + return p; +} + +static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset, + struct avdtp_media_codec_capability *codec, + uint8_t codec_len) +{ + /* Codec specific */ + switch (codec->media_codec_type) { + case A2DP_CODEC_SBC: + return sbc_select_range(codec->data, codec_len, preset->data, + preset->len); + default: + return NULL; + } +} + static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint, struct avdtp_remote_sep *rsep) { struct avdtp_service_capability *service; struct avdtp_media_codec_capability *codec; GSList *l; + uint8_t codec_len; service = avdtp_get_codec(rsep); codec = (struct avdtp_media_codec_capability *) service->data; + codec_len = service->length - sizeof(*codec); for (l = endpoint->presets; l; l = g_slist_next(l)) { struct a2dp_preset *preset = l->data; + int err; - if (check_capabilities(preset, codec, - service->length - sizeof(*codec)) == 0) + err = check_capabilities(preset, codec, codec_len); + if (err == 0) return preset; + + if (err == -ERANGE) + return select_preset_range(preset, codec, codec_len); } return NULL; -- 2.47.3