From patchwork Mon Sep 16 13:28:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805433 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60BA8A95E for ; Mon, 16 Sep 2024 13:28:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; cv=none; b=UQynfKPRegOIsqMztvxo/Y/lElNKV+ay5zkP0YnlMi6lw0uQPV+p3tsN+iOZTl2ZXaaAu9us6q5GrDDuubsyCV6uxft+PieOStB1D9vEfVJmsmD7Yibl17qbc7eL9rZWgZ8ddK8vdIO9XbedCX0slu3BTZYBGTZfpxGruBHXZSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; c=relaxed/simple; bh=ejLoo7BpIgGAYbN9riNyze+0HpZaPohmi7GzRLD0QCQ=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=VDnFYJDJxBGThVz7IYbqBxqpe8tbISg+1rsm3ijxF8bLBpP4jC1Fy/WOJ+34eQeDdQjYUN22ZLkXdd6s0yS8H9kfxtqpn2HAqd6t0afVHE+XthXeaZqBzMqMhBOMgb3AWFMn8WX5OKUKeRXuTREudjMokOFah/e006nTeCE/FdA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=MOVJlkAD; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="MOVJlkAD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493298; bh=ejLoo7BpIgGAYbN9riNyze+0HpZaPohmi7GzRLD0QCQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=MOVJlkADi2ujt7UNzLExpriyXPNoISBskHRDKUm47hhYdB7Wt1/hrLQobKTwi4/Qm LkjXmP5Fp3AczFImakhZZZjilXpRwGXEYwDxFaIlHjFFSGKtWAh0pRUTw4BzfCVtfK qPBnA17WsOuFWV+Htb/V7eRqLHLVfevRU2l8FElOcTooyv5Q2I3+vXg0VMSxpCvgY7 zr5ViC8+vshMc97FFNe+BEriPzF9Hb1XxUf7VKmKPzyX7C60htaEK+qQn+7b/NBMrM ZXMKje5vq55u2UwZ9KxQPf6tLQ/rY0XyuSPYSH0tWl6WoX2uLPHdBQzLVCc7uIO+u2 3bcztubkhjvgw== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 42C6617E1516 for ; Mon, 16 Sep 2024 15:28:18 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 1/9] obexd: Add PSM support to session create Date: Mon, 16 Sep 2024 15:28:05 +0200 Message-Id: <20240916132813.165731-2-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 An OBEX session can be connected to a RFCOMM channel or a L2CAP PSM. --- doc/org.bluez.obex.Client.rst | 4 ++++ doc/org.bluez.obex.Session.rst | 5 +++++ obexd/client/manager.c | 14 ++++++++++---- obexd/client/session.c | 27 ++++++++++++++++++++++++--- obexd/client/session.h | 1 + 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/org.bluez.obex.Client.rst b/doc/org.bluez.obex.Client.rst index 9f77a9abc..5ae7cc5e8 100644 --- a/doc/org.bluez.obex.Client.rst +++ b/doc/org.bluez.obex.Client.rst @@ -52,6 +52,10 @@ object CreateSession(string destination, dict args) Channel to be used. + :uint16 PSM: + + L2CAP PSM to be used. + Possible errors: :org.bluez.obex.Error.InvalidArguments: diff --git a/doc/org.bluez.obex.Session.rst b/doc/org.bluez.obex.Session.rst index 1cef9a53d..fc5f14e5d 100644 --- a/doc/org.bluez.obex.Session.rst +++ b/doc/org.bluez.obex.Session.rst @@ -50,6 +50,11 @@ byte Channel [readonly] Bluetooth channel +uint16 PSM [readonly] +``````````````````````` + + Bluetooth L2CAP PSM + string Target [readonly] ```````````````````````` diff --git a/obexd/client/manager.c b/obexd/client/manager.c index ad1fbb04a..52c00fb0c 100644 --- a/obexd/client/manager.c +++ b/obexd/client/manager.c @@ -107,7 +107,8 @@ done: } static int parse_device_dict(DBusMessageIter *iter, - const char **source, const char **target, uint8_t *channel) + const char **source, const char **target, uint8_t *channel, + uint16_t *psm) { while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; @@ -130,6 +131,10 @@ static int parse_device_dict(DBusMessageIter *iter, if (g_str_equal(key, "Channel") == TRUE) dbus_message_iter_get_basic(&value, channel); break; + case DBUS_TYPE_UINT16: + if (g_str_equal(key, "PSM") == TRUE) + dbus_message_iter_get_basic(&value, psm); + break; } dbus_message_iter_next(iter); @@ -160,6 +165,7 @@ static DBusMessage *create_session(DBusConnection *connection, struct send_data *data; const char *source = NULL, *dest = NULL, *target = NULL; uint8_t channel = 0; + uint16_t psm = 0; dbus_message_iter_init(message, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) @@ -175,8 +181,8 @@ static DBusMessage *create_session(DBusConnection *connection, dbus_message_iter_recurse(&iter, &dict); - parse_device_dict(&dict, &source, &target, &channel); - if (dest == NULL || target == NULL) + parse_device_dict(&dict, &source, &target, &channel, &psm); + if (dest == NULL || target == NULL || (channel && psm)) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); @@ -188,7 +194,7 @@ static DBusMessage *create_session(DBusConnection *connection, data->connection = dbus_connection_ref(connection); data->message = dbus_message_ref(message); - session = obc_session_create(source, dest, target, channel, + session = obc_session_create(source, dest, target, channel, psm, dbus_message_get_sender(message), create_callback, data); if (session != NULL) { diff --git a/obexd/client/session.c b/obexd/client/session.c index 7d8ebb04e..13a834e14 100644 --- a/obexd/client/session.c +++ b/obexd/client/session.c @@ -88,6 +88,7 @@ struct obc_session { char *source; char *destination; uint8_t channel; + uint16_t psm; struct obc_transport *transport; struct obc_driver *driver; char *path; /* Session path */ @@ -471,6 +472,7 @@ static struct obc_session *session_find(const char *source, const char *destination, const char *service, uint8_t channel, + uint16_t psm, const char *owner) { GSList *l; @@ -490,6 +492,9 @@ static struct obc_session *session_find(const char *source, if (channel && session->channel != channel) continue; + if (psm && session->psm != psm) + continue; + if (g_strcmp0(owner, session->owner)) continue; @@ -541,8 +546,9 @@ static int session_connect(struct obc_session *session, } session->id = transport->connect(session->source, session->destination, - driver->uuid, session->channel, - transport_func, callback); + driver->uuid, + session->channel ? session->channel : session->psm, + transport_func, callback); if (session->id == 0) { obc_session_unref(callback->session); g_free(callback); @@ -558,6 +564,7 @@ struct obc_session *obc_session_create(const char *source, const char *destination, const char *service, uint8_t channel, + uint16_t psm, const char *owner, session_callback_t function, void *user_data) @@ -570,7 +577,8 @@ struct obc_session *obc_session_create(const char *source, if (destination == NULL) return NULL; - session = session_find(source, destination, service, channel, owner); + session = session_find(source, destination, service, channel, psm, + owner); if (session != NULL) goto proceed; @@ -598,6 +606,7 @@ struct obc_session *obc_session_create(const char *source, session->source = g_strdup(source); session->destination = g_strdup(destination); session->channel = channel; + session->psm = psm; session->queue = g_queue_new(); session->folder = g_strdup("/"); @@ -762,6 +771,17 @@ static gboolean get_channel(const GDBusPropertyTable *property, return TRUE; } +static gboolean get_psm(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct obc_session *session = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, + &session->psm); + + return TRUE; +} + static const GDBusMethodTable session_methods[] = { { GDBUS_ASYNC_METHOD("GetCapabilities", NULL, GDBUS_ARGS({ "capabilities", "s" }), @@ -794,6 +814,7 @@ static const GDBusPropertyTable session_properties[] = { { "Source", "s", get_source, NULL, source_exists }, { "Destination", "s", get_destination }, { "Channel", "y", get_channel }, + { "PSM", "q", get_psm }, { "Target", "s", get_target, NULL, target_exists }, { } }; diff --git a/obexd/client/session.h b/obexd/client/session.h index 2c646df1a..19c3f3687 100644 --- a/obexd/client/session.h +++ b/obexd/client/session.h @@ -22,6 +22,7 @@ struct obc_session *obc_session_create(const char *source, const char *destination, const char *service, uint8_t channel, + uint16_t psm, const char *owner, session_callback_t function, void *user_data); From patchwork Mon Sep 16 13:28:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805430 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C2A3154C19 for ; Mon, 16 Sep 2024 13:28:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493308; cv=none; b=ZILE5fBvlIex0DBl9CH19ZShyEgNjj3CjBG4R7xiRHHYTvu4gjg9RwSUh5KRjc0suBZhVouZ1Z15H5YyP9/QqdmB+DOXIeKWBFr/lpHeOlk4uEHQEYx3MDvLbBTdbbfv5YG1oM6aGILOuS2Qv85vVeAnB/YCHmt5E5Qa3Vj9Ygw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493308; c=relaxed/simple; bh=Hi6u63RPUed35C0P1BK05kHNmYZFRy1iUO5jFy+S8bE=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=LHtFoujdirP1gRq1kB81WlbDPovGnJ1raUOlaJmtdU4VwAhHOeMFsNpJPf6K2Y/GNimaCjp636Cwp91D2H6im6z3iG5Vs4dlas4143UyBpl+RVwhUemMrQwripSXD7y2sF7jGP+FseYOPRVhqW1ouOGybgV1qS5ubgNZ02cx4+w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=OHGHfQcU; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="OHGHfQcU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493298; bh=Hi6u63RPUed35C0P1BK05kHNmYZFRy1iUO5jFy+S8bE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=OHGHfQcUPAOtorc/kWRyxTuHlkFQ/ZPMxvknau0oNAsFZkDEyATKJiVzoT7D0/oIL wc6TD7tlIXK5FEQHJZnWHfloXjdMtYRGwqM1VwiY1CNMLTC2qvBxCD9/RHRYEn9lG1 ValoGd2g2MAlQIv780YAAxAQBnxtqcM6nmaEVscN9ZzXya0acjwEZ+31Bg9jlB92q4 WdQaxRXzqIsn7KS5ALuUwdN8UxDs0YYRxWW5ZZ4cZUU5X+5dITOG8jVYHqxSzjhMLp 0AGCt6o0k7ujqm1TEq/Do+Fuj/ovmJQkCLVOpfiJbQ02g0uflQ8nlXJmcXDKVeZE5c H/ShZlRgcjSOw== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 80A5D17E1524 for ; Mon, 16 Sep 2024 15:28:18 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 2/9] player: Add OBEX PSM port for cover art support Date: Mon, 16 Sep 2024 15:28:06 +0200 Message-Id: <20240916132813.165731-3-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This parse the AVRCP Target SDP record for the L2CAP PSM to use with the OBEX session to get the cover art. --- doc/org.bluez.MediaPlayer.rst | 6 +++++ profiles/audio/avrcp.c | 51 +++++++++++++++++++++++++++++++---- profiles/audio/player.c | 30 +++++++++++++++++++++ profiles/audio/player.h | 1 + 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/doc/org.bluez.MediaPlayer.rst b/doc/org.bluez.MediaPlayer.rst index 60bd679bb..858344b30 100644 --- a/doc/org.bluez.MediaPlayer.rst +++ b/doc/org.bluez.MediaPlayer.rst @@ -313,3 +313,9 @@ object Playlist ``````````````` Playlist object path. + +uint16 ObexPort [readonly, experimental] +```````````````````````````````````````` + + If present indicates the player can get cover art using BIP over OBEX + on this PSM port. diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index 752e55be3..61558e492 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -118,8 +118,14 @@ #define AVRCP_FEATURE_CATEGORY_2 0x0002 #define AVRCP_FEATURE_CATEGORY_3 0x0004 #define AVRCP_FEATURE_CATEGORY_4 0x0008 -#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010 -#define AVRCP_FEATURE_BROWSING 0x0040 +#define AVRCP_FEATURE_TG_PLAYER_SETTINGS 0x0010 +#define AVRCP_FEATURE_TG_GROUP_NAVIGATION 0x0020 +#define AVRCP_FEATURE_BROWSING 0x0040 +#define AVRCP_FEATURE_TG_MULTIPLE_PLAYER 0x0080 +#define AVRCP_FEATURE_TG_COVERT_ART 0x0100 +#define AVRCP_FEATURE_CT_GET_IMAGE_PROP 0x0080 +#define AVRCP_FEATURE_CT_GET_IMAGE 0x0100 +#define AVRCP_FEATURE_CT_GET_THUMBNAIL 0x0200 #define AVRCP_BATTERY_STATUS_NORMAL 0 #define AVRCP_BATTERY_STATUS_WARNING 1 @@ -254,6 +260,7 @@ struct avrcp_data { struct avrcp_player *player; uint16_t version; int features; + uint16_t obex_port; GSList *players; }; @@ -487,7 +494,7 @@ static sdp_record_t *avrcp_tg_record(bool browsing) AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4 | - AVRCP_FEATURE_PLAYER_SETTINGS ); + AVRCP_FEATURE_TG_PLAYER_SETTINGS); record = sdp_record_alloc(); if (!record) @@ -3522,6 +3529,7 @@ static struct avrcp_player *create_ct_player(struct avrcp *session, return NULL; } + media_player_set_obex_port(mp, session->controller->obex_port); media_player_set_callbacks(mp, &ct_cbs, player); player->user_data = mp; player->destroy = (GDestroyNotify) media_player_destroy; @@ -4006,7 +4014,8 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, if (events == (1 << AVRCP_EVENT_VOLUME_CHANGED)) return FALSE; - if ((session->controller->features & AVRCP_FEATURE_PLAYER_SETTINGS) && + if ((session->controller->features & + AVRCP_FEATURE_TG_PLAYER_SETTINGS) && !(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED))) avrcp_list_player_attributes(session); @@ -4075,8 +4084,9 @@ static struct avrcp_data *data_init(struct avrcp *session, const char *uuid) { struct avrcp_data *data; const sdp_record_t *rec; - sdp_list_t *list; + sdp_list_t *list, *protos; sdp_profile_desc_t *desc; + int port = 0; data = g_new0(struct avrcp_data, 1); @@ -4092,6 +4102,35 @@ static struct avrcp_data *data_init(struct avrcp *session, const char *uuid) sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, &data->features); sdp_list_free(list, free); + if ((g_strcmp0(uuid, AVRCP_TARGET_UUID) != 0) || + !(data->features & AVRCP_FEATURE_TG_COVERT_ART) || + (sdp_get_add_access_protos(rec, &protos) != 0)) + return data; + + /* Get the PSM port from the Additional Protocol Descriptor list + * entry containing OBEX UUID + */ + for (list = protos; list; list = list->next) { + sdp_list_t *p; + + for (p = list->data; p; p = p->next) { + sdp_data_t *seq = p->data; + + if ((sdp_uuid_to_proto(&seq->val.uuid) == OBEX_UUID) && + SDP_IS_UUID(seq->dtd)) { + port = sdp_get_proto_port(list, L2CAP_UUID); + goto done; + } + } + } + +done: + if (port > 0) + data->obex_port = port; + + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); + sdp_list_free(protos, NULL); + return data; } @@ -4189,6 +4228,8 @@ static void controller_init(struct avrcp *session) session->controller = controller; DBG("%p version 0x%04x", controller, controller->version); + if (controller->obex_port) + DBG("%p OBEX PSM 0x%04x", controller, controller->obex_port); service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); btd_service_connecting_complete(service, 0); diff --git a/profiles/audio/player.c b/profiles/audio/player.c index c995697fe..b3a6920fc 100644 --- a/profiles/audio/player.c +++ b/profiles/audio/player.c @@ -88,6 +88,7 @@ struct media_player { struct player_callback *cb; GSList *pending; GSList *folders; + uint16_t obex_port; }; static void append_track(void *key, void *value, void *user_data) @@ -437,6 +438,28 @@ static gboolean get_playlist(const GDBusPropertyTable *property, return TRUE; } +static gboolean obexport_exists(const GDBusPropertyTable *property, + void *data) +{ + struct media_player *mp = data; + + return mp->obex_port != 0; +} + +static gboolean get_obexport(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct media_player *mp = data; + + if (mp->obex_port == 0) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, + &mp->obex_port); + + return TRUE; +} + static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -778,6 +801,8 @@ static const GDBusPropertyTable media_player_properties[] = { { "Browsable", "b", get_browsable, NULL, browsable_exists }, { "Searchable", "b", get_searchable, NULL, searchable_exists }, { "Playlist", "o", get_playlist, NULL, playlist_exists }, + { "ObexPort", "q", get_obexport, NULL, obexport_exists, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; @@ -1997,3 +2022,8 @@ struct media_item *media_player_set_playlist_item(struct media_player *mp, return item; } + +void media_player_set_obex_port(struct media_player *mp, uint16_t port) +{ + mp->obex_port = port; +} diff --git a/profiles/audio/player.h b/profiles/audio/player.h index 74fb7d771..0076c4641 100644 --- a/profiles/audio/player.h +++ b/profiles/audio/player.h @@ -86,6 +86,7 @@ void media_player_set_folder(struct media_player *mp, const char *path, void media_player_set_playlist(struct media_player *mp, const char *name); struct media_item *media_player_set_playlist_item(struct media_player *mp, uint64_t uid); +void media_player_set_obex_port(struct media_player *mp, uint16_t port); struct media_item *media_player_create_folder(struct media_player *mp, const char *name, From patchwork Mon Sep 16 13:28:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805431 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C24614B094 for ; Mon, 16 Sep 2024 13:28:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493308; cv=none; b=XmL5eH7o75hbN7zNDYh/ZLMimM0YTIHRrk6wW/PbGGQe8kU9HC0akulLDlehffMEhi4GTxnALGQ4evvQsqqwbmYEBIYKN8vd3DXQ5Sq+SSM9Dpj0+DAl1sxWP+TeVlKsqMV4YKShYRckGP1fQ61TfNXJsUZtWppvk8CjMV4GWMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493308; c=relaxed/simple; bh=/Yph+mpTwr12Uzsk3dlhqjNxvwAaUSS1c7JzrSfIjo8=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=gglwlK5rkAeMque7/mdI/0lg5HE1/O8DbZL3cuK1Nk5kV/RFaj36gCkujhO/qE/pFKgNLrI86O1qo++VMhz6z1Z7LFfmtB9agWmygGfp7onr/C6nkouJIOsxpp+zHDHoW8ZPLfzuziScT4iDCRsn5wtWvua46UKyoN+fykxBJLg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=G6Fn5saz; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="G6Fn5saz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493298; bh=/Yph+mpTwr12Uzsk3dlhqjNxvwAaUSS1c7JzrSfIjo8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=G6Fn5sazKtf2/zKIJ3U/8V1+ATPAYughn4LREkszldIxQhSIBI4E+gqmLLb+lflRq dZu/ardTleoqwS02x18vVRYpLejAaJekb8jEkf7QMRecG6Z6QxdiPt78FbR2K9jxrr CIWcAaKIW4SN3Z9ru0vbBCion+jPCx8ImYtgvixr/5/50Zfnr/B/ZMI1OmTBfe0Ij+ I09fLYOZtf784dEVtdYo5lf/lXEZnGAlAbAmlR/mU0oY0hRwuewEcqLZA6A4d7fp67 TNb4mVmvfp1ZViSKX8qKrkC/oO0mGhQuKnhfZOk9D9xGCtqJJr8eAQlZtLa08QpVAd D4edZQOocArxA== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id BD2F317E1582 for ; Mon, 16 Sep 2024 15:28:18 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 3/9] player: Add image handle support property Date: Mon, 16 Sep 2024 15:28:07 +0200 Message-Id: <20240916132813.165731-4-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This is part of the metadata when the AVRCP target supports covert art download and a OBEX BIP session is connected. The image handle references the cover art associated to the track, and is valid only during OBEX BIP session. --- doc/org.bluez.MediaPlayer.rst | 5 +++++ monitor/avctp.c | 3 +++ profiles/audio/avrcp.c | 7 ++++++- profiles/audio/avrcp.h | 3 ++- tools/parser/avrcp.c | 3 +++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/org.bluez.MediaPlayer.rst b/doc/org.bluez.MediaPlayer.rst index 858344b30..94f5b8472 100644 --- a/doc/org.bluez.MediaPlayer.rst +++ b/doc/org.bluez.MediaPlayer.rst @@ -237,6 +237,11 @@ dict Track [readonly] Track duration in milliseconds + :string ImgHandle: [experimental] + + Track image handle, available and valid only during the lifetime of an + OBEX BIP connection to the ObexPort. + object Device [readonly] ```````````````````````` diff --git a/monitor/avctp.c b/monitor/avctp.c index c59e93b20..4da448750 100644 --- a/monitor/avctp.c +++ b/monitor/avctp.c @@ -156,6 +156,7 @@ #define AVRCP_MEDIA_ATTRIBUTE_TOTAL 0x05 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 +#define AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE 0x08 /* play status */ #define AVRCP_PLAY_STATUS_STOPPED 0x00 @@ -582,6 +583,8 @@ static const char *mediattr2str(uint32_t attr) return "Genre"; case AVRCP_MEDIA_ATTRIBUTE_DURATION: return "Track duration"; + case AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE: + return "Imaging handle"; default: return "Reserved"; } diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index 61558e492..fe24b5a92 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -417,7 +417,8 @@ static sdp_record_t *avrcp_ct_record(bool browsing) uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | - AVRCP_FEATURE_CATEGORY_4); + AVRCP_FEATURE_CATEGORY_4 | + AVRCP_FEATURE_CT_GET_THUMBNAIL); record = sdp_record_alloc(); if (!record) @@ -883,6 +884,8 @@ static const char *metadata_to_str(uint32_t id) return "NumberOfTracks"; case AVRCP_MEDIA_ATTRIBUTE_DURATION: return "Duration"; + case AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE: + return "ImgHandle"; } return NULL; @@ -1197,6 +1200,8 @@ static uint32_t str_to_metadata(const char *str) return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS; else if (strcasecmp(str, "Duration") == 0) return AVRCP_MEDIA_ATTRIBUTE_DURATION; + else if (strcasecmp(str, "ImgHandle") == 0) + return AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE; return 0; } diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h index dcc580e37..59117e946 100644 --- a/profiles/audio/avrcp.h +++ b/profiles/audio/avrcp.h @@ -46,7 +46,8 @@ #define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 -#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION +#define AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE 0x08 +#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE /* play status */ #define AVRCP_PLAY_STATUS_STOPPED 0x00 diff --git a/tools/parser/avrcp.c b/tools/parser/avrcp.c index e73a6317e..d574c7ee3 100644 --- a/tools/parser/avrcp.c +++ b/tools/parser/avrcp.c @@ -160,6 +160,7 @@ #define AVRCP_MEDIA_ATTRIBUTE_TOTAL 0x5 #define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x6 #define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x7 +#define AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE 0x08 /* play status */ #define AVRCP_PLAY_STATUS_STOPPED 0x00 @@ -933,6 +934,8 @@ static const char *mediattr2str(uint32_t attr) return "Genre"; case AVRCP_MEDIA_ATTRIBUTE_DURATION: return "Track duration"; + case AVRCP_MEDIA_ATTRIBUTE_IMG_HANDLE: + return "Imaging handle"; default: return "Reserved"; } From patchwork Mon Sep 16 13:28:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805432 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 60BEC155730 for ; Mon, 16 Sep 2024 13:28:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493309; cv=none; b=CpShwFG2eW8Rf2nkWHhUJOG3LAliYikp0UDO1Ra75Wd2Pi4UKTnvcUlsOOpOLudcZ8N/aF5kjraBozmLM/xdNloVD2mLjurDsxRPoOslTv1lhUQRh7GDfeo0hm7aWrEpiMRMKhoB083BGYZ4qj9kR6Ee0oNfcBl/PqOEFHEZlro= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493309; c=relaxed/simple; bh=9TFkBkML4V8Vx8C7Ff0t4ywdRdVjnnXl/oXt2y1Qx6w=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=rOKFCkV9YYEb6cNuL+2Mnj3nT+EdUhUkyzbQY2HU8e+ixLG0dCW9nPGqurE3jRwjggOwaoL7grTA9qQCLsu2b+qagGmesD0R3CTxLdXOJWh+oLbSASg/nrh5v4u5rBYaUcGYc7CXi7/T4N/Bj5TOCNEv7mEFHktTCsQnYeRoNtw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=SVfy+CO4; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="SVfy+CO4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493299; bh=9TFkBkML4V8Vx8C7Ff0t4ywdRdVjnnXl/oXt2y1Qx6w=; h=From:To:Subject:Date:In-Reply-To:References:From; b=SVfy+CO4kZm3mJw3IGOceGI089LTWfuKMiNxxacqVG9NGqV2sW6sWldK37gRwdNE/ ThYPzbh2TCggNAfg+6z2VE9vukovnjGbPBS2+DHVpo4rRMf9KNIp0fLIaYVug6eAw6 9Q4AzViYwiiJcg9EePaNAgdiKqsdlk8TYNVlAfjB0JjVEXCUCplBYMNFI9Z23CtawI pGsxpEibFODRjmQ+gnvNL3Wv9zqIyHc6WQmDvRqr2qpEx+/72HcW/V4CswpyPGqtDw 3fLmB+HhExyy8GqOqgo19wUQ6VNAsbJUAalBS8ZLK84nmjqlIY/loZu00Y90dfOt+5 l9PS737ORCZiA== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 0726A17E1584 for ; Mon, 16 Sep 2024 15:28:19 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 4/9] obexd: Add support for specific headers in obex transfer Date: Mon, 16 Sep 2024 15:28:08 +0200 Message-Id: <20240916132813.165731-5-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The BIP is using the Image Handle Tag header to set the file handle to transfer. --- obexd/client/transfer.c | 19 +++++++++++++++++++ obexd/client/transfer.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c index a7a85a0c0..879d67d58 100644 --- a/obexd/client/transfer.c +++ b/obexd/client/transfer.c @@ -57,6 +57,7 @@ struct obc_transfer { GObex *obex; uint8_t status; GObexApparam *apparam; + GSList *headers; guint8 op; struct transfer_callback *callback; DBusConnection *conn; @@ -400,6 +401,11 @@ static const GDBusPropertyTable obc_transfer_properties[] = { { } }; +static void header_free(void *data, void *user_data) +{ + g_obex_header_free(data); +} + static void obc_transfer_free(struct obc_transfer *transfer) { DBG("%p", transfer); @@ -441,6 +447,8 @@ static void obc_transfer_free(struct obc_transfer *transfer) if (transfer->obex) g_obex_unref(transfer->obex); + g_slist_foreach(transfer->headers, header_free, NULL); + g_slist_free(transfer->headers); g_free(transfer->callback); g_free(transfer->owner); g_free(transfer->filename); @@ -820,6 +828,12 @@ static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type, strlen(transfer->type) + 1); + while (transfer->headers) { + hdr = transfer->headers->data; + g_obex_packet_add_header(req, hdr); + transfer->headers = g_slist_remove(transfer->headers, hdr); + } + if (transfer->apparam != NULL) { hdr = g_obex_header_new_apparam(transfer->apparam); g_obex_packet_add_header(req, hdr); @@ -974,3 +988,8 @@ gint64 obc_transfer_get_size(struct obc_transfer *transfer) { return transfer->size; } + +void obc_transfer_add_header(struct obc_transfer *transfer, void *data) +{ + transfer->headers = g_slist_append(transfer->headers, data); +} diff --git a/obexd/client/transfer.h b/obexd/client/transfer.h index 323332a62..1ed195984 100644 --- a/obexd/client/transfer.h +++ b/obexd/client/transfer.h @@ -47,3 +47,5 @@ gint64 obc_transfer_get_size(struct obc_transfer *transfer); DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer, DBusMessage *message); + +void obc_transfer_add_header(struct obc_transfer *transfer, void *data); From patchwork Mon Sep 16 13:28:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805435 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EB2FC156F53 for ; Mon, 16 Sep 2024 13:28:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; cv=none; b=NiIkpbsOTXe5e2aUb04xxMY4DesxHsPPEFweMVPPYisLlwXyYIiNagvvskaVdoEO2BMC0iMfVTmP31GDR4XqYZrSdTW1fMeluUvB/8cGODP92k6PseAdxMIO9UmB+Uss2eWDM3ZP26gpPmrxaehVDL1y2SaL1jSL1soitUR/17A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; c=relaxed/simple; bh=IoSmNIwK4ihzqHMDeV5NbgCvdwSYo+nR7XkU8RpSR2k=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=ZOnx9dgehtHN9ebjciXVFRASzLjKGV9SrI8kdvEQIzHnLC53f9A7SiIABl3lBoG5+GEmHNPmW4seuNjqdab4+mbFOfjwEAiJJ9VFNoZCkQNtjg0ou/Q42W21ZzZnkWXNNApXBtnSAEHniUP9kfRpQoRI3apHHUr4+Jo+TQ2PoH8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=elyEToUs; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="elyEToUs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493299; bh=IoSmNIwK4ihzqHMDeV5NbgCvdwSYo+nR7XkU8RpSR2k=; h=From:To:Subject:Date:In-Reply-To:References:From; b=elyEToUsxhY7ErEMzcUfajeA5LlKPllmd5HWfkS+tg5QGdq50LDEoqKUX+yq8G/5a rVbwZB+irSLoL/BRPj6BaO1LJKHRjfavQsusPSPh5RWHyjGTQZqR0uMOpuSPEs2C3o qckwpvj5ZJ7cZLqieBr8zAyaL6CmxxBi5GL83Kcl1B3eDylHPYD6+zSnsCHos1Pdam Vszks9Tg5JRSLdWuKjvMETj5ErssZHADCMvwMI7SViDnCXs//5PkZMpQFUgafiWqiH w4gsGwEWjCkgskzuuVS5uE6GK+HwYKKR/DKHl/WFaIyi7iqaRcX2d+5BBiY+UPL/hU 50fuOLhiJtj7Q== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 46B6F17E35C9 for ; Mon, 16 Sep 2024 15:28:19 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 5/9] obexd: Add BIP client for AVRCP cover art download Date: Mon, 16 Sep 2024 15:28:09 +0200 Message-Id: <20240916132813.165731-6-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The cover art image handle is available in the metadata of the track when the OBEX BIP session is connected to the PSM port provided in AVRCP SDP record and available as org.bluez.MediaPlayer property. This service allows to get the thumbnail. --- Makefile.obexd | 1 + obexd/client/bip.c | 171 +++++++++++++++++++++++++++++++++++++++++ obexd/client/bip.h | 12 +++ obexd/client/manager.c | 2 + 4 files changed, 186 insertions(+) create mode 100644 obexd/client/bip.c create mode 100644 obexd/client/bip.h diff --git a/Makefile.obexd b/Makefile.obexd index 4cdce73af..866147dd1 100644 --- a/Makefile.obexd +++ b/Makefile.obexd @@ -81,6 +81,7 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ obexd/client/ftp.h obexd/client/ftp.c \ obexd/client/opp.h obexd/client/opp.c \ obexd/client/map.h obexd/client/map.c \ + obexd/client/bip.h obexd/client/bip.c \ obexd/client/map-event.h obexd/client/map-event.c \ obexd/client/transfer.h obexd/client/transfer.c \ obexd/client/transport.h obexd/client/transport.c \ diff --git a/obexd/client/bip.c b/obexd/client/bip.c new file mode 100644 index 000000000..f4af2834b --- /dev/null +++ b/obexd/client/bip.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "gdbus/gdbus.h" +#include "gobex/gobex.h" + +#include "obexd/src/log.h" +#include "transfer.h" +#include "session.h" +#include "driver.h" +#include "bip.h" + +#define OBEX_BIP_AVRCP_UUID \ + "\x71\x63\xDD\x54\x4A\x7E\x11\xE2\xB4\x7C\x00\x50\xC2\x49\x00\x48" +#define OBEX_BIP_AVRCP_UUID_LEN 16 + +#define BIP_AVRCP_INTERFACE "org.bluez.obex.BipAvrcp1" +#define ERROR_INTERFACE "org.bluez.obex.Error" +#define BIP_AVRCP_UUID "0000111A-0000-1000-8000-00805f9b34fb" + +#define IMG_HANDLE_TAG 0x30 + +static DBusConnection *conn; + +struct bip_avrcp_data { + struct obc_session *session; +}; + +static DBusMessage *get_image_thumbnail(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + const char *handle = NULL, *image_path = NULL; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GError *err = NULL; + + DBG(""); + + if (dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &image_path, + DBUS_TYPE_STRING, &handle, + DBUS_TYPE_INVALID) == FALSE) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + return reply; + } + + transfer = obc_transfer_get("x-bt/img-thm", NULL, image_path, &err); + if (transfer == NULL) + goto fail; + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, &err)) + goto fail; + + return obc_transfer_create_dbus_reply(transfer, message); + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + +static const GDBusMethodTable bip_avrcp_methods[] = { + { GDBUS_ASYNC_METHOD("GetImageThumbnail", + GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), + GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), + get_image_thumbnail) }, + { } +}; + +static void bip_avrcp_free(void *data) +{ + struct bip_avrcp_data *bip_avrcp = data; + + obc_session_unref(bip_avrcp->session); + g_free(bip_avrcp); +} + +static int bip_avrcp_probe(struct obc_session *session) +{ + struct bip_avrcp_data *bip_avrcp; + const char *path; + + path = obc_session_get_path(session); + + DBG("%s", path); + + bip_avrcp = g_try_new0(struct bip_avrcp_data, 1); + if (!bip_avrcp) + return -ENOMEM; + + bip_avrcp->session = obc_session_ref(session); + + if (!g_dbus_register_interface(conn, path, BIP_AVRCP_INTERFACE, + bip_avrcp_methods, + NULL, NULL, + bip_avrcp, bip_avrcp_free)) { + bip_avrcp_free(bip_avrcp); + return -ENOMEM; + } + + return 0; +} + +static void bip_avrcp_remove(struct obc_session *session) +{ + const char *path = obc_session_get_path(session); + + DBG("%s", path); + + g_dbus_unregister_interface(conn, path, BIP_AVRCP_INTERFACE); +} + +static struct obc_driver bip_avrcp = { + .service = "BIP-AVRCP", + .uuid = BIP_AVRCP_UUID, + .target = OBEX_BIP_AVRCP_UUID, + .target_len = OBEX_BIP_AVRCP_UUID_LEN, + .probe = bip_avrcp_probe, + .remove = bip_avrcp_remove +}; + +int bip_init(void) +{ + int err; + + DBG(""); + + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if (!conn) + return -EIO; + + err = obc_driver_register(&bip_avrcp); + if (err < 0) + goto failed; + + return 0; + +failed: + dbus_connection_unref(conn); + conn = NULL; + return err; +} + +void bip_exit(void) +{ + DBG(""); + + dbus_connection_unref(conn); + conn = NULL; + + obc_driver_unregister(&bip_avrcp); +} diff --git a/obexd/client/bip.h b/obexd/client/bip.h new file mode 100644 index 000000000..18e3360f3 --- /dev/null +++ b/obexd/client/bip.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +int bip_init(void); +void bip_exit(void); diff --git a/obexd/client/manager.c b/obexd/client/manager.c index 52c00fb0c..52f4d0179 100644 --- a/obexd/client/manager.c +++ b/obexd/client/manager.c @@ -32,6 +32,7 @@ #include "pbap.h" #include "sync.h" #include "map.h" +#include "bip.h" #include "manager.h" #define CLIENT_INTERFACE "org.bluez.obex.Client1" @@ -258,6 +259,7 @@ static const struct obc_module { { "pbap", pbap_init, pbap_exit }, { "sync", sync_init, sync_exit }, { "map", map_init, map_exit }, + { "bip", bip_init, bip_exit }, { } }; From patchwork Mon Sep 16 13:28:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805437 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA833156F3B for ; Mon, 16 Sep 2024 13:28:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493312; cv=none; b=quLN6/oL7GHTkJQm2/xgJmpIXzaf9L6be2ZQSKHEN0/++vQG810HTKj+IySkNFVHCwwD2GFXKkZw8/xxb7PI75hIyLuseEbJYMhN/c2oTpT8+AHPW9dw9W3ERgulS59gNlBhKQNEUDyviaMZ+xRLTb/VT+NlXR0HaN3sA9YZJAY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493312; c=relaxed/simple; bh=cTm5eZankIaOE/lK3yF6Ozx2145lzl3z7Bc8RYCtlL4=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=faffx7ZCXfAWI1u43J7yk/gWYv/Q14Y/S8AOXj09DEHZ9JgLq8FBCr2X9C1pEolsEpZsRMxGeeH4tVoC5AMLV+yojqH+v6sZVxX2rj0/FE47RceNbksuVgskVfYYjtrsUS5TEYZcHgljzBrV1Vo/7U5st5LLsku8kQiqucYBMcY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=cObyMRKo; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="cObyMRKo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493299; bh=cTm5eZankIaOE/lK3yF6Ozx2145lzl3z7Bc8RYCtlL4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=cObyMRKoGv1wsTCHWlxxwutAiAbkdh8Vq26brh81JHrisjJCeAJUcVbrV9E9FQF0e UEm+BB1zxmPUTluIPmLEOLw+yEJm+OGIwcKjgVd6pfp2DyPHM0DHbWcA7puXCrOp1C IEcLTPXZZkruRHMg1m4vjpXBv2ohvYf1qVpO3Wc57ghPr6oe0vbT5GYq3ozgJy3ub8 PGHKFzpuFrznCmDi3hCSC7jV7Pr/1+VldXVvczd/ik1SWj4mteBK3upodQfsP4esSZ vYV9H/U8TD1qB9kcS0WZEeJzyjrp+9ik/DdmD+JSJH013vP43i3ZnBDucCauzZY6nP XjN6GL7Cg9mKQ== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 8614317E35CB for ; Mon, 16 Sep 2024 15:28:19 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 6/9] obexd: Add GetImageProperties to bip-avrcp Date: Mon, 16 Sep 2024 15:28:10 +0200 Message-Id: <20240916132813.165731-7-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This allows to get the different version of the image provided by the remote device to chose the one to use with GetImage. This bip-common.[ch] files are based on previous work done by Jakub Adamek for GSoC 2011, see [1] and [2]. [1] https://www.bluez.org/gsoc-basic-image-profilebip/ [2] https://github.com/enkait/Basic-Imaging-Profile-in-obexd/blob/gsoc_final/plugins/bip_util.c --- Makefile.obexd | 1 + obexd/client/bip-common.c | 781 ++++++++++++++++++++++++++++++++++++++ obexd/client/bip-common.h | 19 + obexd/client/bip.c | 101 +++++ 4 files changed, 902 insertions(+) create mode 100644 obexd/client/bip-common.c create mode 100644 obexd/client/bip-common.h diff --git a/Makefile.obexd b/Makefile.obexd index 866147dd1..74dd977a0 100644 --- a/Makefile.obexd +++ b/Makefile.obexd @@ -82,6 +82,7 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ obexd/client/opp.h obexd/client/opp.c \ obexd/client/map.h obexd/client/map.c \ obexd/client/bip.h obexd/client/bip.c \ + obexd/client/bip-common.h obexd/client/bip-common.c \ obexd/client/map-event.h obexd/client/map-event.c \ obexd/client/transfer.h obexd/client/transfer.c \ obexd/client/transport.h obexd/client/transport.c \ diff --git a/obexd/client/bip-common.c b/obexd/client/bip-common.c new file mode 100644 index 000000000..9e96c81d8 --- /dev/null +++ b/obexd/client/bip-common.c @@ -0,0 +1,781 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * Based on previous work done by Jakub Adamek for GSoC 2011 + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "gobex/gobex.h" + +#include "obexd/src/log.h" +#include "bip-common.h" + +#define HANDLE_LEN 7 +#define HANDLE_LIMIT 10000000 + +struct encconv_pair { + gchar *bip, *im; +}; + +struct encconv_pair encconv_table[] = { + { "JPEG", "JPEG" }, + { "GIF", "GIF" }, + { "WBMP", "WBMP" }, + { "PNG", "PNG" }, + { "JPEG2000", "JP2" }, + { "BMP", "BMP" }, + { } +}; + +static const gchar *convBIP2IM(const gchar *encoding) +{ + struct encconv_pair *et = encconv_table; + + while (et->bip) { + if (g_strcmp0(encoding, et->bip) == 0) + return et->im; + et++; + } + return NULL; +} + +static gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, + unsigned int *upper_ret, + gboolean *fixed_ratio_ret) +{ + static regex_t no_range; + static regex_t range; + static regex_t range_fixed; + static int regex_initialized; + unsigned int lower[2], upper[2]; + gboolean fixed_ratio = FALSE; + + if (!regex_initialized) { + regcomp(&no_range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regcomp(&range, "^([[:digit:]]{1,5})\\*([[:digit:]]{1,5})" + "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regcomp(&range_fixed, "^([[:digit:]]{1,5})\\*\\*" + "-([[:digit:]]{1,5})\\*([[:digit:]]{1,5})$", + REG_EXTENDED); + regex_initialized = 1; + } + if (dim == NULL) + return FALSE; + if (regexec(&no_range, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u*%u", &lower[0], &lower[1]) != 2) + return FALSE; + upper[0] = lower[0]; + upper[1] = lower[1]; + fixed_ratio = FALSE; + } else if (regexec(&range, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u*%u-%u*%u", &lower[0], &lower[1], + &upper[0], &upper[1]) != 4) + return FALSE; + fixed_ratio = FALSE; + } else if (regexec(&range_fixed, dim, 0, NULL, 0) == 0) { + if (sscanf(dim, "%u**-%u*%u", &lower[0], &upper[0], + &upper[1]) != 3) + return FALSE; + lower[1] = 0; + fixed_ratio = TRUE; + } else { + return FALSE; + } + if (lower[0] > 65535 || lower[1] > 65535 || + upper[0] > 65535 || upper[1] > 65535) + return FALSE; + if (lower_ret == NULL || upper_ret == NULL || fixed_ratio_ret == NULL) + return TRUE; + if (upper[0] < lower[0] || upper[1] < lower[1]) + return FALSE; + lower_ret[0] = lower[0]; + lower_ret[1] = lower[1]; + upper_ret[0] = upper[0]; + upper_ret[1] = upper[1]; + *fixed_ratio_ret = fixed_ratio; + + return TRUE; +} + +static gboolean verify_unsignednumber(const char *size) +{ + static regex_t unumber; + static int regex_initialized; + + if (!regex_initialized) { + regcomp(&unumber, "^[[:digit:]]+$", REG_EXTENDED); + regex_initialized = 1; + } + if (regexec(&unumber, size, 0, NULL, 0) != 0) + return FALSE; + + return TRUE; +} + +static uint64_t parse_unsignednumber(const char *size) +{ + if (!verify_unsignednumber(size)) + return 0; + + return g_ascii_strtoll(size, NULL, 10); +} + +char *transforms[] = { + "crop", + "stretch", + "fill", + NULL +}; + +static gboolean verify_transform(const char *transform) +{ + char **str = transforms; + + while (*str != NULL) { + if (g_str_equal(transform, *str)) + return TRUE; + str++; + } + return FALSE; +} + +static char *parse_transform_list(const char *transform) +{ + char **args = NULL, **arg = NULL; + gboolean used[3] = { FALSE, FALSE, FALSE }; + + if (transform == NULL) + return NULL; + if (strlen(transform) == 0) + return NULL; + args = g_strsplit(transform, " ", 0); + for (arg = args; *arg != NULL; arg++) { + char *t = *arg; + + if (!verify_transform(t)) { + g_strfreev(args); + return NULL; + } + switch (t[0]) { + case 's': + if (used[0]) + goto failure; + used[0] = TRUE; + break; + case 'c': + if (used[1]) + goto failure; + used[1] = TRUE; + break; + case 'f': + if (used[2]) + goto failure; + used[2] = TRUE; + break; + } + } + g_strfreev(args); + return g_strdup(transform); +failure: + g_strfreev(args); + return NULL; +} + +static time_t parse_iso8601_bip(const gchar *str, int len) +{ + gchar *tstr; + struct tm tm; + gint nr; + gchar tz; + time_t time; + time_t tz_offset = 0; + + if (str == NULL) + return -1; + + memset(&tm, 0, sizeof(struct tm)); + + /* According to spec the time doesn't have to be null terminated */ + if (str[len - 1] != '\0') { + tstr = g_malloc(len + 1); + strncpy(tstr, str, len); + tstr[len] = '\0'; + } else + tstr = g_strdup(str); + + nr = sscanf(tstr, "%04u%02u%02uT%02u%02u%02u%c", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec, + &tz); + + g_free(tstr); + + /* Fixup the tm values */ + tm.tm_year -= 1900; /* Year since 1900 */ + tm.tm_mon--; /* Months since January, values 0-11 */ + tm.tm_isdst = -1; /* Daylight savings information not avail */ + + if (nr < 6) { + /* Invalid time format */ + return -1; + } + + time = mktime(&tm); + +#if defined(HAVE_TM_GMTOFF) + tz_offset = tm.tm_gmtoff; +#elif defined(HAVE_TIMEZONE) + tz_offset = -timezone; + if (tm.tm_isdst > 0) + tz_offset += 3600; +#endif + + if (nr == 7) { /* Date/Time was in localtime (to remote device) + * already. Since we don't know anything about the + * timezone on that one we won't try to apply UTC offset + */ + time += tz_offset; + } + + return time; +} + +static int parse_handle(const char *data) +{ + int handle; + char *ptr; + + if (data == NULL) + return -1; + if (strlen(data) != HANDLE_LEN) + return -1; + handle = strtol(data, &ptr, 10); + if (ptr != data + HANDLE_LEN) + return -1; + if (handle < 0 || handle >= HANDLE_LIMIT) + return -1; + return handle; +} + +struct native_prop { + char *encoding, *pixel; + uint64_t size; +}; + +struct variant_prop { + char *encoding, *pixel, *transform; + uint64_t maxsize; +}; + +struct att_prop { + char *content_type, *charset, *name; + uint64_t size; + time_t ctime, mtime; +}; + +struct prop_object { + char *handle, *name; + GSList *native, *variant, *att; +}; + +static void free_native_prop(struct native_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->encoding); + g_free(prop->pixel); + g_free(prop); +} + +static void free_variant_prop(struct variant_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->encoding); + g_free(prop->pixel); + g_free(prop->transform); + g_free(prop); +} + +static void free_att_prop(struct att_prop *prop) +{ + DBG(""); + + if (prop == NULL) + return; + g_free(prop->content_type); + g_free(prop->charset); + g_free(prop->name); + g_free(prop); +} + +static void free_prop_object(struct prop_object *object) +{ + GSList *list; + + DBG(""); + + if (object == NULL) + return; + for (list = object->native; list != NULL; list = g_slist_next(list)) + free_native_prop(list->data); + for (list = object->variant; list != NULL; list = g_slist_next(list)) + free_variant_prop(list->data); + for (list = object->att; list != NULL; list = g_slist_next(list)) + free_att_prop(list->data); + g_slist_free(object->native); + g_slist_free(object->variant); + g_slist_free(object->att); + g_free(object->handle); + g_free(object->name); + g_free(object); +} + +static gboolean parse_attrib_native(struct native_prop *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "encoding")) { + if (convBIP2IM(value) == NULL) + goto invalid; + prop->encoding = g_strdup(value); + } else if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(value, NULL, NULL, NULL)) + goto invalid; + prop->pixel = g_strdup(value); + } else if (g_str_equal(key, "size")) { + prop->size = parse_unsignednumber(value); + if (prop->size == 0) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static gboolean parse_attrib_variant(struct variant_prop *prop, + const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "encoding")) { + if (convBIP2IM(value) == NULL) + goto invalid; + prop->encoding = g_strdup(value); + } else if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(value, NULL, NULL, NULL)) + goto invalid; + prop->pixel = g_strdup(value); + } else if (g_str_equal(key, "maxsize")) { + prop->maxsize = parse_unsignednumber(value); + if (prop->maxsize == 0) + goto invalid; + } else if (g_str_equal(key, "transform")) { + prop->transform = parse_transform_list(value); + if (prop->transform == NULL) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static gboolean parse_attrib_att(struct att_prop *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "content-type")) { + prop->content_type = g_strdup(value); + } else if (g_str_equal(key, "charset")) { + prop->charset = g_strdup(value); + } else if (g_str_equal(key, "name")) { + prop->name = g_strdup(value); + } else if (g_str_equal(key, "size")) { + prop->size = parse_unsignednumber(value); + if (prop->size == 0) + goto invalid; + } else if (g_str_equal(key, "created")) { + prop->ctime = parse_iso8601_bip(value, strlen(value)); + if (prop->ctime == -1) + goto invalid; + } else if (g_str_equal(key, "modified")) { + prop->mtime = parse_iso8601_bip(value, strlen(value)); + if (prop->mtime == -1) + goto invalid; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static struct att_prop *parse_elem_att(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct att_prop *prop = g_new0(struct att_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_att(prop, *key, *values, gerr)) { + free_att_prop(prop); + return NULL; + } + } + return prop; +} + +static struct variant_prop *parse_elem_variant(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct variant_prop *prop = g_new0(struct variant_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_variant(prop, *key, *values, gerr)) { + free_variant_prop(prop); + return NULL; + } + } + if (prop->transform == NULL) + prop->transform = g_strdup("stretch crop fill"); + return prop; +} + +static struct native_prop *parse_elem_native(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct native_prop *prop = g_new0(struct native_prop, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_native(prop, *key, *values, gerr)) { + free_native_prop(prop); + return NULL; + } + } + return prop; +} + +static gboolean parse_attrib_prop(struct prop_object *prop, const gchar *key, + const gchar *value, GError **gerr) +{ + DBG(""); + + if (g_str_equal(key, "handle")) { + if (parse_handle(value) < 0) + goto invalid; + prop->handle = g_strdup(value); + } else if (g_str_equal(key, "friendly-name")) { + prop->name = g_strdup(value); + } else if (g_str_equal(key, "version")) { + // pass; + } else { + g_set_error(gerr, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, NULL); + return FALSE; + } + return TRUE; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); + return FALSE; +} + +static struct prop_object *parse_elem_prop(const gchar **names, + const gchar **values, GError **gerr) +{ + gchar **key; + struct prop_object *prop = g_new0(struct prop_object, 1); + + DBG(""); + + for (key = (gchar **) names; *key; key++, values++) { + if (!parse_attrib_prop(prop, *key, *values, gerr)) { + free_prop_object(prop); + return NULL; + } + } + return prop; +} + +static void prop_element(GMarkupParseContext *ctxt, + const gchar *element, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **gerr) +{ + struct prop_object **obj = user_data; + + DBG(""); + + if (g_str_equal(element, "image-properties")) { + if (*obj != NULL) { + free_prop_object(*obj); + *obj = NULL; + goto invalid; + } + *obj = parse_elem_prop(names, values, gerr); + } else if (g_str_equal(element, "native")) { + struct native_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_native(names, values, gerr); + (*obj)->native = g_slist_append((*obj)->native, prop); + } else if (g_str_equal(element, "variant")) { + struct variant_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_variant(names, values, gerr); + (*obj)->variant = g_slist_append((*obj)->variant, prop); + } else if (g_str_equal(element, "attachment")) { + struct att_prop *prop; + + if (*obj == NULL) + goto invalid; + prop = parse_elem_att(names, values, gerr); + (*obj)->att = g_slist_append((*obj)->att, prop); + } else { + if (*obj != NULL) { + free_prop_object(*obj); + *obj = NULL; + } + goto invalid; + } + + return; +invalid: + g_set_error(gerr, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + NULL); +} + +static const GMarkupParser properties_parser = { + prop_element, + NULL, + NULL, + NULL, + NULL +}; + +struct prop_object *parse_properties(char *data, unsigned int length, + int *err) +{ + struct prop_object *prop = NULL; + gboolean status; + GError *gerr = NULL; + GMarkupParseContext *ctxt = g_markup_parse_context_new( + &properties_parser, 0, &prop, NULL); + + DBG(""); + + if (err != NULL) + *err = 0; + status = g_markup_parse_context_parse(ctxt, data, length, &gerr); + g_markup_parse_context_free(ctxt); + if (!status) { + if (err != NULL) + *err = -EINVAL; + free_prop_object(prop); + prop = NULL; + } + return prop; +} + +gboolean verify_properties(struct prop_object *obj) +{ + GSList *list; + + if (obj->handle == NULL) + return FALSE; + + for (list = obj->native; list != NULL; list = g_slist_next(list)) { + struct native_prop *prop = list->data; + + if (prop->encoding == NULL || prop->pixel == NULL) + return FALSE; + } + + for (list = obj->variant; list != NULL; list = g_slist_next(list)) { + struct variant_prop *prop = list->data; + + if (prop->encoding == NULL || prop->pixel == NULL) + return FALSE; + } + + for (list = obj->att; list != NULL; list = g_slist_next(list)) { + struct att_prop *prop = list->data; + + if (prop->content_type == NULL || prop->name == NULL) + return FALSE; + } + + return TRUE; +} + +void append_properties(DBusMessageIter *args, struct prop_object *obj) +{ + DBusMessageIter dict, iter; + GSList *list; + + dbus_message_iter_open_container(args, DBUS_TYPE_ARRAY, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict); + + dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &iter); + g_dbus_dict_append_entry(&iter, "handle", DBUS_TYPE_STRING, + &obj->handle); + g_dbus_dict_append_entry(&iter, "name", DBUS_TYPE_STRING, &obj->name); + dbus_message_iter_close_container(&dict, &iter); + + for (list = obj->native; list != NULL; list = g_slist_next(list)) { + struct native_prop *prop = list->data; + static char *native_str = "native"; + + dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &native_str); + if (prop->encoding) + g_dbus_dict_append_entry(&iter, "encoding", + DBUS_TYPE_STRING, + &prop->encoding); + if (prop->pixel) + g_dbus_dict_append_entry(&iter, "pixel", + DBUS_TYPE_STRING, + &prop->pixel); + if (prop->size) + g_dbus_dict_append_entry(&iter, "size", + DBUS_TYPE_UINT64, + &prop->size); + dbus_message_iter_close_container(&dict, &iter); + } + + for (list = obj->variant; list != NULL; list = g_slist_next(list)) { + struct variant_prop *prop = list->data; + static char *variant_str = "variant"; + + dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &variant_str); + if (prop->encoding) + g_dbus_dict_append_entry(&iter, "encoding", + DBUS_TYPE_STRING, + &prop->encoding); + if (prop->pixel) + g_dbus_dict_append_entry(&iter, "pixel", + DBUS_TYPE_STRING, + &prop->pixel); + if (prop->maxsize) + g_dbus_dict_append_entry(&iter, "maxsize", + DBUS_TYPE_UINT64, + &prop->maxsize); + if (prop->transform) + g_dbus_dict_append_entry(&iter, "transformation", + DBUS_TYPE_STRING, + &prop->transform); + dbus_message_iter_close_container(&dict, &iter); + } + + for (list = obj->att; list != NULL; list = g_slist_next(list)) { + struct att_prop *prop = list->data; + static char *attachment_str = "attachment"; + + dbus_message_iter_open_container(&dict, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &iter); + g_dbus_dict_append_entry(&iter, "type", DBUS_TYPE_STRING, + &attachment_str); + if (prop->content_type) + g_dbus_dict_append_entry(&iter, "content-type", + DBUS_TYPE_STRING, + &prop->content_type); + if (prop->charset) + g_dbus_dict_append_entry(&iter, "charset", + DBUS_TYPE_STRING, + &prop->charset); + if (prop->name) + g_dbus_dict_append_entry(&iter, "name", + DBUS_TYPE_STRING, + &prop->name); + if (prop->size) + g_dbus_dict_append_entry(&iter, "size", + DBUS_TYPE_UINT64, + &prop->size); + if (prop->ctime) + g_dbus_dict_append_entry(&iter, "ctime", + DBUS_TYPE_UINT64, + &prop->ctime); + if (prop->mtime) + g_dbus_dict_append_entry(&iter, "mtime", + DBUS_TYPE_UINT64, + &prop->mtime); + dbus_message_iter_close_container(&dict, &iter); + } + + dbus_message_iter_close_container(args, &dict); +} diff --git a/obexd/client/bip-common.h b/obexd/client/bip-common.h new file mode 100644 index 000000000..0fee54636 --- /dev/null +++ b/obexd/client/bip-common.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * OBEX Client + * + * Copyright (C) 2024 Collabora Ltd. + * + * + */ + +#include +#include "gdbus/gdbus.h" + +struct prop_object; + +struct prop_object *parse_properties(char *data, unsigned int length, + int *err); +gboolean verify_properties(struct prop_object *obj); +void append_properties(DBusMessageIter *args, struct prop_object *obj); diff --git a/obexd/client/bip.c b/obexd/client/bip.c index f4af2834b..6e1fa642a 100644 --- a/obexd/client/bip.c +++ b/obexd/client/bip.c @@ -20,6 +20,7 @@ #include "transfer.h" #include "session.h" #include "driver.h" +#include "bip-common.h" #include "bip.h" #define OBEX_BIP_AVRCP_UUID \ @@ -38,6 +39,102 @@ struct bip_avrcp_data { struct obc_session *session; }; +static void image_properties_complete_cb(struct obc_session *session, + struct obc_transfer *transfer, + GError *err, void *user_data) +{ + DBusMessage *message = user_data; + DBusMessage *reply = NULL; + DBusMessageIter iter; + char *contents = NULL; + size_t size; + int perr; + struct prop_object *prop = NULL; + + if (err != NULL) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "%s", err->message); + goto done; + } + + perr = obc_transfer_get_contents(transfer, &contents, &size); + if (perr < 0) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error reading contents: %s", + strerror(-perr)); + goto done; + } + + prop = parse_properties(contents, size, &perr); + if (prop == NULL) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error parsing contents: %s", + strerror(-perr)); + goto done; + } + + if (!verify_properties(prop)) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Error verifying contents"); + goto done; + } + + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &iter); + append_properties(&iter, prop); + +done: + g_dbus_send_message(conn, reply); + g_free(contents); + dbus_message_unref(message); +} + +static DBusMessage *get_image_properties(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + const char *handle = NULL; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GError *err = NULL; + + DBG(""); + + if (dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &handle, + DBUS_TYPE_INVALID) == FALSE) { + reply = g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + return reply; + } + + transfer = obc_transfer_get("x-bt/img-properties", NULL, NULL, &err); + if (transfer == NULL) + goto fail; + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + if (!obc_session_queue(bip_avrcp->session, transfer, + image_properties_complete_cb, message, &err)) + goto fail; + + dbus_message_ref(message); + + return NULL; + +fail: + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + static DBusMessage *get_image_thumbnail(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -79,6 +176,10 @@ fail: } static const GDBusMethodTable bip_avrcp_methods[] = { + { GDBUS_ASYNC_METHOD("GetImageProperties", + GDBUS_ARGS({ "handle", "s"}), + GDBUS_ARGS({ "properties", "aa{sv}" }), + get_image_properties) }, { GDBUS_ASYNC_METHOD("GetImageThumbnail", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), From patchwork Mon Sep 16 13:28:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805438 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 626F6157E6B for ; Mon, 16 Sep 2024 13:28:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493314; cv=none; b=rBQcP5ZmMnPrUAe6u2JaMdB53jqK5J/DPT9HRIMQ5s9wTi08mQikXaVr4V+GhZw1mcINr55tiobm5pFawfUWtRff0nkamzx/ycL+Ve1AH7Xowm39p0mqh8HYMBe4t4CzeEEKQjVxTi6jRPthzXpjRNjwOL7DXci1hQtcksC+s4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493314; c=relaxed/simple; bh=+Of5tE2yu6nSxIrKUrCHTxJXNCCBaJhc/Haj3FftXuc=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=TuRmpAqKyc8xM1v+m0vKNMrlYVuApWH82dkPkA32eXWBdeC5QRMFsqEu1PDVr3Q4XFnvuheXqjPik4n0mI4kUghX2wk9HHjDrp3SE/PcL6qRC4aXtOvsoLb96jNUFVpsAk8CRdW8c8IDcQUK/yws/T2yTgHDhs5lwIquIS/QZVM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=k45/a0Yg; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="k45/a0Yg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493299; bh=+Of5tE2yu6nSxIrKUrCHTxJXNCCBaJhc/Haj3FftXuc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=k45/a0YgMp1Y5IOBM1X/eOPzxD/gvhK5maYrYajm8iqffOHPOFkRrNBJXqLfJrPA7 w3WM+3N6282tuypPxC0WoGtCzN3+skle+IplEbglS2X0EcWPOhaqssIOBSwksPIGAf wxBAnHQQj8avO9bC4a3smK4biPvVUv6cyi5yZrNIVgl3Atl0ERdCTXqngX9FaOrrUY GS+/bce8dIshQaRTeFEptHCHyBSy2dPy08Q1vpZKaGXtOsHBJmjR8TxOg5sq3CyzOg IXb05dQPctqdlDD99+3tGR1qEwQtFfF+gOeoqpJNwrUgHlv5Kz+aeCRxNJdak7LPUj JSdJd8QX13mHg== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id CAFE017E35CC for ; Mon, 16 Sep 2024 15:28:19 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 7/9] obexd: Add GetImage to bip-avrcp Date: Mon, 16 Sep 2024 15:28:11 +0200 Message-Id: <20240916132813.165731-8-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Retrieves the image corresponding to the handle and the description, as one of the descriptions retrieved by GetImageProperties, and store it in a local file. If the "transform" property description exists it should be set to one of the value listed by GetImageProperties for this description. --- obexd/client/bip-common.c | 21 ++++- obexd/client/bip-common.h | 5 ++ obexd/client/bip.c | 168 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) diff --git a/obexd/client/bip-common.c b/obexd/client/bip-common.c index 9e96c81d8..613b52ceb 100644 --- a/obexd/client/bip-common.c +++ b/obexd/client/bip-common.c @@ -49,7 +49,7 @@ static const gchar *convBIP2IM(const gchar *encoding) return NULL; } -static gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, +gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, unsigned int *upper_ret, gboolean *fixed_ratio_ret) { @@ -139,6 +139,18 @@ char *transforms[] = { NULL }; +gboolean verify_encoding(const char *encoding) +{ + struct encconv_pair *et = encconv_table; + + while (et->bip) { + if (g_strcmp0(encoding, et->bip) == 0) + return TRUE; + et++; + } + return FALSE; +} + static gboolean verify_transform(const char *transform) { char **str = transforms; @@ -151,6 +163,13 @@ static gboolean verify_transform(const char *transform) return FALSE; } +char *parse_transform(const char *transform) +{ + if (!verify_transform(transform)) + return NULL; + return g_strdup(transform); +} + static char *parse_transform_list(const char *transform) { char **args = NULL, **arg = NULL; diff --git a/obexd/client/bip-common.h b/obexd/client/bip-common.h index 0fee54636..6e7aac375 100644 --- a/obexd/client/bip-common.h +++ b/obexd/client/bip-common.h @@ -13,6 +13,11 @@ struct prop_object; +gboolean parse_pixel_range(const gchar *dim, unsigned int *lower_ret, + unsigned int *upper_ret, + gboolean *fixed_ratio_ret); +gboolean verify_encoding(const char *encoding); +char *parse_transform(const char *transform); struct prop_object *parse_properties(char *data, unsigned int length, int *err); gboolean verify_properties(struct prop_object *obj); diff --git a/obexd/client/bip.c b/obexd/client/bip.c index 6e1fa642a..86df3f407 100644 --- a/obexd/client/bip.c +++ b/obexd/client/bip.c @@ -32,6 +32,14 @@ #define BIP_AVRCP_UUID "0000111A-0000-1000-8000-00805f9b34fb" #define IMG_HANDLE_TAG 0x30 +#define IMG_DESC_TAG 0x71 + +#define EOL_CHARS "\n" +#define IMG_DESC_BEGIN "" EOL_CHARS +#define IMG_BEGIN "" EOL_CHARS +#define IMG_DESC_END "" EOL_CHARS static DBusConnection *conn; @@ -175,11 +183,171 @@ fail: return reply; } +static gboolean parse_get_image_dict(DBusMessage *msg, char **path, + char **handle, char **pixel, + char **encoding, uint64_t *maxsize, + char **transform) +{ + DBusMessageIter iter, array; + + DBG(""); + + *path = NULL; + *handle = NULL; + *pixel = NULL; + *encoding = NULL; + *transform = NULL; + + dbus_message_iter_init(msg, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto failed; + dbus_message_iter_get_basic(&iter, path); + *path = g_strdup(*path); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto failed; + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, handle); + *handle = g_strdup(*handle); + dbus_message_iter_next(&iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + goto failed; + + dbus_message_iter_recurse(&iter, &array); + + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key, *val; + + dbus_message_iter_recurse(&array, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + return FALSE; + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + switch (dbus_message_iter_get_arg_type(&value)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&value, &val); + if (g_str_equal(key, "pixel")) { + if (!parse_pixel_range(val, NULL, NULL, NULL)) + goto failed; + *pixel = g_strdup(val); + } else if (g_str_equal(key, "encoding")) { + if (!verify_encoding(val)) + goto failed; + *encoding = g_strdup(val); + if (*encoding == NULL) + goto failed; + } else if (g_str_equal(key, "transformation")) { + *transform = parse_transform(val); + if (*transform == NULL) + goto failed; + } + break; + case DBUS_TYPE_UINT64: + if (g_str_equal(key, "maxsize") == TRUE) { + dbus_message_iter_get_basic(&value, maxsize); + if (*maxsize == 0) + goto failed; + } + break; + } + dbus_message_iter_next(&array); + } + + if (*pixel == NULL) + *pixel = strdup(""); + if (*encoding == NULL) + *encoding = strdup(""); + + DBG("pixel: '%s' encoding: '%s' maxsize: '%lu' transform: '%s'", + *pixel, *encoding, *maxsize, *transform + ); + + return TRUE; +failed: + g_free(*path); + g_free(*handle); + g_free(*pixel); + g_free(*encoding); + g_free(*transform); + return FALSE; +} + +static DBusMessage *get_image(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct bip_avrcp_data *bip_avrcp = user_data; + char *handle = NULL, *image_path = NULL, *transform = NULL, + *encoding = NULL, *pixel = NULL; + uint64_t maxsize; + struct obc_transfer *transfer; + GObexHeader *header; + DBusMessage *reply = NULL; + GString *descriptor = NULL; + GError *err = NULL; + + DBG(""); + + if (!parse_get_image_dict(message, &image_path, &handle, &pixel, + &encoding, &maxsize, &transform)) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + transfer = obc_transfer_get("x-bt/img-img", NULL, image_path, &err); + if (transfer == NULL) { + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", + "%s", + err->message); + g_error_free(err); + goto fail; + } + + header = g_obex_header_new_unicode(IMG_HANDLE_TAG, handle); + obc_transfer_add_header(transfer, header); + + descriptor = g_string_new(IMG_DESC_BEGIN); + g_string_append_printf(descriptor, IMG_BEGIN, encoding, pixel); + if (transform != NULL) + g_string_append_printf(descriptor, IMG_TRANSFORM, transform); + g_string_append(descriptor, IMG_END); + descriptor = g_string_append(descriptor, IMG_DESC_END); + header = g_obex_header_new_bytes(IMG_DESC_TAG, descriptor->str, + descriptor->len); + obc_transfer_add_header(transfer, header); + g_string_free(descriptor, TRUE); + + if (!obc_session_queue(bip_avrcp->session, transfer, NULL, NULL, + &err)) { + reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", + "%s", + err->message); + g_error_free(err); + goto fail; + } + + reply = obc_transfer_create_dbus_reply(transfer, message); + +fail: + g_free(handle); + g_free(image_path); + g_free(transform); + g_free(encoding); + g_free(pixel); + return reply; +} + static const GDBusMethodTable bip_avrcp_methods[] = { { GDBUS_ASYNC_METHOD("GetImageProperties", GDBUS_ARGS({ "handle", "s"}), GDBUS_ARGS({ "properties", "aa{sv}" }), get_image_properties) }, + { GDBUS_ASYNC_METHOD("GetImage", + GDBUS_ARGS({ "file", "s" }, { "handle", "s"}, + {"properties", "a{sv}"}), + GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), + get_image) }, { GDBUS_ASYNC_METHOD("GetImageThumbnail", GDBUS_ARGS({ "file", "s" }, { "handle", "s"}), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), From patchwork Mon Sep 16 13:28:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805434 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F060155730 for ; Mon, 16 Sep 2024 13:28:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; cv=none; b=Gf4yhVlmQugqjNKMg2BouLtE0EKCGJhW7F6IJ5X0L3PjenJXsvJUvirtUZObarXXEeCBXCsqUBn0LicFHl6ED0iRpwUR9bIuRDXaFX62jAKx14c3rbTTBJtc8ovC08pEVF84/53LM2+x5dtPz8AhPHLwxP06Q/sQOSU5Ob2U6Qc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493311; c=relaxed/simple; bh=xfgCIiupl1IIWSNbxZ4zd48NLjvNMVHp8UG6XCfe7NI=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=K4bTMWFa/vgAPkE2b4E5KCbtmpo5M2mSou9xjV0YlbCyXeBsYTHERBtoXyzqTG7jZchTaxFZGoWjRagX95dTraHVzirmjfm4SRA2YamFeFXe50ivJf2UklTyLgKdfmeT/8NmEcpCZJyDp4pZwF7piAFFp9jB/awo8WVWIcssFic= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=m5bTJimB; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="m5bTJimB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493300; bh=xfgCIiupl1IIWSNbxZ4zd48NLjvNMVHp8UG6XCfe7NI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=m5bTJimB00LSMYROMJNs2b3SBG1uNM/1/0efA3EqgdHCONAIl9f4qL3pn6eeU7wt+ 9PGBfwrakRUDvFBRBbDWBuzmHJyfedfepxvKMwejSMKshkrRhcMynD24QxuDjnSeQJ xSh5OG2W428TdPsBiL+NrAYoMR7at7vQ6Wk6X+Xz90X0vOy30w6KuC6XB2N9vNlhT/ U4HRHoCLA2/9YIDxBkYkMy2Ix1PLivzh8o2MNnt8kagE19VfRE8+3bamfk5dqeVcGi Ylc8UVoarDauT7GIxLqjJL5H0ezdONG3mqj92RFulAlrb3yhniOXEE3WVrRAxJSdNZ NFrynfSOOGkrg== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 13CFC17E35CF for ; Mon, 16 Sep 2024 15:28:20 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 8/9] avrcp: Update controller SDP record with cover art support Date: Mon, 16 Sep 2024 15:28:12 +0200 Message-Id: <20240916132813.165731-9-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- profiles/audio/avrcp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index fe24b5a92..c2c901a65 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -413,12 +413,14 @@ static sdp_record_t *avrcp_ct_record(bool browsing) sdp_record_t *record; sdp_data_t *psm[2], *version, *features; uint16_t lp = AVCTP_CONTROL_PSM; - uint16_t avctp_ver = 0x0103; + uint16_t avctp_ver = 0x0106; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | - AVRCP_FEATURE_CATEGORY_2 | - AVRCP_FEATURE_CATEGORY_3 | - AVRCP_FEATURE_CATEGORY_4 | - AVRCP_FEATURE_CT_GET_THUMBNAIL); + AVRCP_FEATURE_CATEGORY_2 | + AVRCP_FEATURE_CATEGORY_3 | + AVRCP_FEATURE_CATEGORY_4 | + AVRCP_FEATURE_CT_GET_IMAGE_PROP | + AVRCP_FEATURE_CT_GET_IMAGE | + AVRCP_FEATURE_CT_GET_THUMBNAIL); record = sdp_record_alloc(); if (!record) From patchwork Mon Sep 16 13:28:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= X-Patchwork-Id: 13805436 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 78562156F57 for ; Mon, 16 Sep 2024 13:28:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493312; cv=none; b=khqI6REebtWPlKj0gH6IrGicX6gq8nJcxR5Ef5A5gKOg5WzeBjnuOj16XGdJKGPKtrtFnHPktVuRNZ2Uo8AV28abW05DwXjljbV2iwlr8fOMMUdG34Khm/M6S24qdpbvAVHyqme4wHlNrUxiSVjdrSTYjMNkOfSjg2YgUFONN70= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726493312; c=relaxed/simple; bh=Xws2x5vTC4bCXGRYndHuuAiGQVgXfiPyG+VzH86zf0Y=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=C1hZFFZVJCTvz/IqMHK1D8cC3ghdKFEhING0iD2Rf3SQgpOAwaAP5NQA0jldXiyQ8V2MoVxTPmfPqHO/o4fB6Q7sWZpuFs61qCq4thwPdO+Hep91hI4y2ACQ9HB0KzpAHE6FzRSNDivJqqqVZ9jH5GkQsotPAuy8ioEtRUKGTzU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=Aw9d/efQ; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="Aw9d/efQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1726493300; bh=Xws2x5vTC4bCXGRYndHuuAiGQVgXfiPyG+VzH86zf0Y=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Aw9d/efQQ8ouAB28LPxzs2iIG4PTiJSOZfricwk1xC826hBGNyu8awrvz1wWabLYj aUDumIMDMDA5M0zSjSIX9G3PyctIFR4Yh+CS8hZpe0uU5BBmPwyIfs4bX6jTrS0D/9 +I5Vn6JYsSvcKJImRv007/TCFpGYnkPs0Y5p8xztsuWi2DoCaF4DVpREuIV4IanJ2I l2tvKNFJ84hQNcZT6fMewQdlN/ZoUzXgNtTqEUDhaW1Ig2zKHvmE+r1e5yQ3XBbp6T HVjw1dCDy29h+mpsZv/ciSzAQNKo6GVITwKJ+H6MGZvt4PSy0sO/Pq83AXMCfyc+V9 L6LHuOa1Ak3ag== Received: from fdanis-XPS-13-9370.. (67.227.121.78.rev.sfr.net [78.121.227.67]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 5177817E35D3 for ; Mon, 16 Sep 2024 15:28:20 +0200 (CEST) From: =?utf-8?q?Fr=C3=A9d=C3=A9ric_Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ v3 9/9] doc: Add description of org.bluez.obex.BipAvrcp Date: Mon, 16 Sep 2024 15:28:13 +0200 Message-Id: <20240916132813.165731-10-frederic.danis@collabora.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240916132813.165731-1-frederic.danis@collabora.com> References: <20240916132813.165731-1-frederic.danis@collabora.com> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This new interface allows to get the image referenced in the audio metadata ImgHandle available in org.bluez.MediaPlayer track properties. The image handle is only available in track info if an OBEX session is connected to the ObexPort port provided in org.bluez.MediaPlayer properties. --- Makefile.am | 9 +++-- doc/org.bluez.obex.BipAvrcp.rst | 72 +++++++++++++++++++++++++++++++++ doc/org.bluez.obex.Client.rst | 1 + 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 doc/org.bluez.obex.BipAvrcp.rst diff --git a/Makefile.am b/Makefile.am index 88044aa05..3a9728782 100644 --- a/Makefile.am +++ b/Makefile.am @@ -382,7 +382,8 @@ man_MANS += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ - doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 + doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ + doc/org.bluez.obex.BipAvrcp.5 endif manual_pages += src/bluetoothd.8 manual_pages += doc/l2cap.7 doc/rfcomm.7 @@ -415,7 +416,8 @@ manual_pages += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.PhonebookAccess.5 \ doc/org.bluez.obex.MessageAccess.5 \ doc/org.bluez.obex.Message.5 \ - doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 + doc/org.bluez.obex.AgentManager.5 doc/org.bluez.obex.Agent.5 \ + doc/org.bluez.obex.BipAvrcp.5 EXTRA_DIST += src/genbuiltin src/bluetooth.conf \ src/main.conf profiles/network/network.conf \ @@ -497,7 +499,8 @@ EXTRA_DIST += doc/org.bluez.obex.Client.rst doc/org.bluez.obex.Session.rst \ doc/org.bluez.obex.PhonebookAccess.rst \ doc/org.bluez.obex.MessageAccess.rst \ doc/org.bluez.obex.Message.rst \ - doc/org.bluez.obex.AgentManager.rst doc/org.bluez.obex.Agent.rst + doc/org.bluez.obex.AgentManager.rst doc/org.bluez.obex.Agent.rst \ + doc/org.bluez.obex.BipAvrcp.rst EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \ doc/pts-opp.txt diff --git a/doc/org.bluez.obex.BipAvrcp.rst b/doc/org.bluez.obex.BipAvrcp.rst new file mode 100644 index 000000000..46b12f645 --- /dev/null +++ b/doc/org.bluez.obex.BipAvrcp.rst @@ -0,0 +1,72 @@ +======================= +org.bluez.obex.BipAvrcp +======================= + +-------------------------------------------------- +BlueZ D-Bus OBEX BipAvrcp API documentation +-------------------------------------------------- + +:Version: BlueZ +:Date: August 2024 +:Manual section: 5 +:Manual group: Linux System Administration + +Interface +========= + +:Service: org.bluez.obex +:Interface: org.bluez.obex.BipAvrcp1 [experimental] +:Object path: [Session object path] + +Methods +------- + +object, dict GetImage(string targetfile, string handle, dict description) +````````````````````````````````````````````````````````````````````````` + + Retrieves the image corresponding to the handle and the description, as + one of the descriptions retrieved by GetImageProperties, and store it in + a local file. + + If the "transform" property description exists it should be set to one + of the value listed by GetImageProperties for this description. + + If description is an empty dict, the native image will be retrieved; + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: + +array{dict} GetImageproperties(string handle) +````````````````````````````````````````````` + + Retrieves the image properties corresponding to the handle. + + The first dict entry is mandatory and correspond to 'handle' and 'name' + of the image. + + The second dict entry is mandatory and correspond to the native description + ('type':'native') of the image. + + The following dict entries are optional and correspond to variant + descriptions of the image. If the 'transform' entry exists in the + description, it lists the available possible image transformations and + should be set to one of them before using the description as parameter + to GetImage. + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: + +object, dict GetImageThumbnail(string targetfile, string handle) +```````````````````````````````````````````````````````````````` + + Retrieves the image thumbnail corresponding to the handle and store it in + a local file. + + Possible errors: + + :org.bluez.obex.Error.InvalidArguments: + :org.bluez.obex.Error.Failed: diff --git a/doc/org.bluez.obex.Client.rst b/doc/org.bluez.obex.Client.rst index 5ae7cc5e8..f20dd5baa 100644 --- a/doc/org.bluez.obex.Client.rst +++ b/doc/org.bluez.obex.Client.rst @@ -43,6 +43,7 @@ object CreateSession(string destination, dict args) :"opp": :"pbap": :"sync": + :"bip-avrcp": :string Source: