avrcp: Implement Press/Hold/Release method for MediaPlayer1
authorArchie Pusaka <apusaka@chromium.org>
Thu, 3 Sep 2020 03:50:39 +0000 (11:50 +0800)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 12 Apr 2021 09:00:50 +0000 (14:30 +0530)
This allows us to send any passthrough command, complete with the
support to hold down the key. Using Press() will automatically
release the key, while using Hold() will keep the key held until
an explicit call to Release() is received.

This doesn't allow us to hold multiple keys simultaneously, since
according to the AV/C Panel Subunit Specification, part 9.4, when the
target receive a pressed command without receiving a release command
of the previous key, it will be treated as if the release command is
sent but not received.

Previously, the rewind and fast_forward keys are unique in terms that
they are treated as holdable keys, this patch preserves that behavior
of calling Rewind() and FastForward(). A rewind event which is
reported via the new Press() method will automatically be released
instead.

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
profiles/audio/avctp.c
profiles/audio/avctp.h
profiles/audio/avrcp.c
profiles/audio/control.c
profiles/audio/player.c
profiles/audio/player.h

index da0cb3d..e194bf5 100644 (file)
  */
 #define AVC_PRESS_TIMEOUT      2
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+/* We need to send hold event before AVC_PRESS time runs out */
+#define AVC_HOLD_TIMEOUT       1
+#endif
+
 #define CONTROL_TIMEOUT                10
 #define BROWSING_TIMEOUT       10
 
@@ -191,6 +196,9 @@ struct avctp_channel {
 struct key_pressed {
        uint16_t op;
        guint timer;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       bool hold;
+#endif
 };
 
 struct avctp {
@@ -1573,6 +1581,9 @@ static struct avctp *avctp_get_internal(struct btd_device *device)
        session->state = AVCTP_STATE_DISCONNECTED;
        session->uinput = -1;
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       session->key.op = AVC_INVALID;
+#endif
        server->sessions = g_slist_append(server->sessions, session);
 
        return session;
@@ -1951,7 +1962,9 @@ static gboolean repeat_timeout(gpointer user_data)
 {
        struct avctp *session = user_data;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        avctp_passthrough_release(session, session->key.op);
+#endif
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        avctp_passthrough_press(session, session->key.op);
 
@@ -1961,21 +1974,40 @@ static gboolean repeat_timeout(gpointer user_data)
 #endif
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void release_pressed(struct avctp *session)
+#else
+static int release_pressed(struct avctp *session)
+#endif
 {
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (session->key.op != AVC_FAST_FORWARD && session->key.op != AVC_REWIND)
 #endif
-       avctp_passthrough_release(session, session->key.op);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       avctp_passthrough_release(session, session->key.op);
+#else
+       int ret = avctp_passthrough_release(session, session->key.op);
+#endif
        if (session->key.timer > 0)
                g_source_remove(session->key.timer);
 
        session->key.timer = 0;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       session->key.op = AVC_INVALID;
+       session->key.hold = false;
+
+       return ret;
+#endif
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static bool set_pressed(struct avctp *session, uint8_t op)
+#else
+static bool hold_pressed(struct avctp *session, uint8_t op)
+#endif
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (session->key.timer > 0) {
                if (session->key.op == op)
                        return TRUE;
@@ -1983,10 +2015,18 @@ static bool set_pressed(struct avctp *session, uint8_t op)
        }
 
        if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+#else
+       if (session->key.op != op || !session->key.hold)
+#endif
                return FALSE;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        session->key.op = op;
        session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+#else
+       if (session->key.timer == 0)
+               session->key.timer = g_timeout_add_seconds(AVC_HOLD_TIMEOUT,
+#endif
                                                        repeat_timeout,
                                                        session);
 
@@ -1998,26 +2038,61 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
                                        uint8_t *operands, size_t operand_count,
                                        void *user_data)
 {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       uint8_t op = operands[0];
+#endif
+
        if (code != AVC_CTYPE_ACCEPTED)
                return FALSE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (set_pressed(session, operands[0]))
+#else
+       if (hold_pressed(session, op))
+#endif
                return FALSE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        avctp_passthrough_release(session, operands[0]);
-
+#else
+       if (op == session->key.op)
+               release_pressed(session);
+#endif
        return FALSE;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 int avctp_send_passthrough(struct avctp *session, uint8_t op)
+#else
+int avctp_send_passthrough(struct avctp *session, uint8_t op, bool hold)
+#endif
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        /* Auto release if key pressed */
        if (session->key.timer > 0)
-               release_pressed(session);
+#else
+       if (op & 0x80)
+               return -EINVAL;
 
+       /* Release previously unreleased key */
+       if (session->key.op != AVC_INVALID && session->key.op != op)
+#endif
+               release_pressed(session);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+       session->key.op = op;
+       session->key.hold = hold;
+#endif
        return avctp_passthrough_press(session, op);
 }
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+int avctp_send_release_passthrough(struct avctp *session)
+{
+       if (session->key.op != AVC_INVALID)
+               return release_pressed(session);
+
+       return 0;
+}
+#endif
+
 #ifdef TIZEN_FEATURE_BLUEZ_A2DP_MULTISTREAM
 static int avctp_passthrough_press_fast(struct avctp *session, uint8_t op)
 {
index f1c760c..f045dab 100755 (executable)
 #define AVC_YELLOW                     0x7c
 #define AVC_VENDOR_UNIQUE              0x7e
 
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+#define AVC_INVALID                    0xff
+#endif
 struct avctp;
 
 typedef enum {
@@ -187,7 +190,13 @@ unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
                                                GDestroyNotify destroy);
 gboolean avctp_unregister_browsing_pdu_handler(unsigned int id);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
+#else
+int avctp_send_passthrough(struct avctp *session, uint8_t op, bool hold);
+int avctp_send_release_passthrough(struct avctp *session);
+#endif
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 int avctp_send_release_passthrough(struct avctp *session, uint8_t op);
 #endif
index 9cf1842..c22b0aa 100644 (file)
@@ -3223,22 +3223,56 @@ static bool ct_set_setting(struct media_player *mp, const char *key,
 
 static int ct_press(struct avrcp_player *player, uint8_t op)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        int err;
        struct avrcp *session;
-
+#else
+       struct avrcp *session = player->sessions->data;
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        session = player->sessions->data;
+#endif
        if (session == NULL)
                return -ENOTCONN;
 
        set_ct_player(session, player);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        err = avctp_send_passthrough(session->conn, op);
        if (err < 0)
                return err;
 
        return 0;
+#else
+       return avctp_send_passthrough(session->conn, op, false);
+#endif
+}
+
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+static int ct_hold(struct avrcp_player *player, uint8_t op)
+{
+       struct avrcp *session = player->sessions->data;
+
+       if (session == NULL)
+               return -ENOTCONN;
+
+       set_ct_player(session, player);
+
+       return avctp_send_passthrough(session->conn, op, true);
 }
 
+static int ct_release(struct avrcp_player *player)
+{
+       struct avrcp *session = player->sessions->data;
+
+       if (session == NULL)
+               return -ENOTCONN;
+
+       set_ct_player(session, player);
+
+       return avctp_send_release_passthrough(session->conn);
+}
+#endif
+
 #if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(TIZEN_FEATURE_BLUEZ_A2DP_MULTISTREAM)
 static int ct_press_send_atonce(struct avrcp_player *player, uint8_t op)
 {
@@ -3373,14 +3407,37 @@ static int ct_fast_forward(struct media_player *mp, void *user_data)
 {
        struct avrcp_player *player = user_data;
 
-       return ct_press(player, AVC_FAST_FORWARD);
+       return ct_hold(player, AVC_FAST_FORWARD);
 }
 
 static int ct_rewind(struct media_player *mp, void *user_data)
 {
        struct avrcp_player *player = user_data;
 
-       return ct_press(player, AVC_REWIND);
+       return ct_hold(player, AVC_REWIND);
+}
+
+static int ct_press_key(struct media_player *mp, uint8_t avc_key,
+                                                               void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       return ct_press(player, avc_key);
+}
+
+static int ct_hold_key(struct media_player *mp, uint8_t avc_key,
+                                                               void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       return ct_hold(player, avc_key);
+}
+
+static int ct_release_key(struct media_player *mp, void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       return ct_release(player);
 }
 #endif
 
@@ -3769,6 +3826,9 @@ static const struct media_player_callback ct_cbs = {
 #else
        .fast_forward   = ct_fast_forward,
        .rewind         = ct_rewind,
+       .press          = ct_press_key,
+       .hold           = ct_hold_key,
+       .release        = ct_release_key,
 #endif
        .list_items     = ct_list_items,
        .change_folder  = ct_change_folder,
index 4ab1f9b..a129bff 100755 (executable)
@@ -136,7 +136,11 @@ int control_disconnect(struct btd_service *service)
 }
 
 static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                                                uint8_t op, void *data)
+#else
+                                       uint8_t op, bool hold, void *data)
+#endif
 {
        struct control *control = data;
        int err;
@@ -147,7 +151,11 @@ static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
        if (!control->target)
                return btd_error_not_supported(msg);
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        err = avctp_send_passthrough(control->session, op);
+#else
+       err = avctp_send_passthrough(control->session, op, hold);
+#endif
        if (err < 0)
                return btd_error_failed(msg, strerror(-err));
 
@@ -157,55 +165,91 @@ static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
 static DBusMessage *control_volume_up(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_VOLUME_UP, data);
+#else
+       return key_pressed(conn, msg, AVC_VOLUME_UP, false, data);
+#endif
 }
 
 static DBusMessage *control_volume_down(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_VOLUME_DOWN, data);
+#else
+       return key_pressed(conn, msg, AVC_VOLUME_DOWN, false, data);
+#endif
 }
 
 static DBusMessage *control_play(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_PLAY, data);
+#else
+       return key_pressed(conn, msg, AVC_PLAY, false, data);
+#endif
 }
 
 static DBusMessage *control_pause(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_PAUSE, data);
+#else
+       return key_pressed(conn, msg, AVC_PAUSE, false, data);
+#endif
 }
 
 static DBusMessage *control_stop(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_STOP, data);
+#else
+       return key_pressed(conn, msg, AVC_STOP, false, data);
+#endif
 }
 
 static DBusMessage *control_next(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_FORWARD, data);
+#else
+       return key_pressed(conn, msg, AVC_FORWARD, false, data);
+#endif
 }
 
 static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_BACKWARD, data);
+#else
+       return key_pressed(conn, msg, AVC_BACKWARD, false, data);
+#endif
 }
 
 static DBusMessage *control_fast_forward(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_FAST_FORWARD, data);
+#else
+       return key_pressed(conn, msg, AVC_FAST_FORWARD, true, data);
+#endif
 }
 
 static DBusMessage *control_rewind(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        return key_pressed(conn, msg, AVC_REWIND, data);
+#else
+       return key_pressed(conn, msg, AVC_REWIND, true, data);
+#endif
 }
 
 static gboolean control_property_get_connected(
index 2390684..0737cfd 100755 (executable)
@@ -683,6 +683,68 @@ static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
+
+static DBusMessage *media_player_press(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+       uint8_t avc_key;
+
+       if (cb->cbs->press == NULL)
+               return btd_error_not_supported(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       err = cb->cbs->press(mp, avc_key, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_hold(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+       uint8_t avc_key;
+
+       if (cb->cbs->hold == NULL)
+               return btd_error_not_supported(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       err = cb->cbs->hold(mp, avc_key, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_release(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct media_player *mp = data;
+       struct player_callback *cb = mp->cb;
+       int err;
+
+       if (cb->cbs->release == NULL)
+               return btd_error_not_supported(msg);
+
+       err = cb->cbs->release(mp, cb->user_data);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
 #endif
 static void parse_folder_list(gpointer data, gpointer user_data)
 {
@@ -826,6 +888,11 @@ static const GDBusMethodTable media_player_methods[] = {
 #else
        { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
        { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
+       { GDBUS_METHOD("Press", GDBUS_ARGS({"avc_key", "y"}), NULL,
+                                                       media_player_press) },
+       { GDBUS_METHOD("Hold", GDBUS_ARGS({"avc_key", "y"}), NULL,
+                                                       media_player_hold) },
+       { GDBUS_METHOD("Release", NULL, NULL, media_player_release) },
 #endif
        { }
 };
index 61d04ae..26858e5 100755 (executable)
@@ -45,35 +45,40 @@ struct media_player;
 struct media_item;
 
 struct media_player_callback {
-       bool (*set_setting) (struct media_player *mp, const char *key,
+       bool (*set_setting)(struct media_player *mp, const char *key,
                                const char *value, void *user_data);
-       int (*play) (struct media_player *mp, void *user_data);
-       int (*pause) (struct media_player *mp, void *user_data);
-       int (*stop) (struct media_player *mp, void *user_data);
-       int (*next) (struct media_player *mp, void *user_data);
-       int (*previous) (struct media_player *mp, void *user_data);
+       int (*play)(struct media_player *mp, void *user_data);
+       int (*pause)(struct media_player *mp, void *user_data);
+       int (*stop)(struct media_player *mp, void *user_data);
+       int (*next)(struct media_player *mp, void *user_data);
+       int (*previous)(struct media_player *mp, void *user_data);
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       int (*press_fast_forward) (struct media_player *mp, void *user_data);
-       int (*release_fast_forward) (struct media_player *mp, void *user_data);
-       int (*press_rewind) (struct media_player *mp, void *user_data);
-       int (*release_rewind) (struct media_player *mp, void *user_data);
-       int (*volume_up) (struct media_player *mp, void *user_data);
-       int (*volume_down) (struct media_player *mp, void *user_data);
+       int (*press_fast_forward)(struct media_player *mp, void *user_data);
+       int (*release_fast_forward)(struct media_player *mp, void *user_data);
+       int (*press_rewind)(struct media_player *mp, void *user_data);
+       int (*release_rewind)(struct media_player *mp, void *user_data);
+       int (*volume_up)(struct media_player *mp, void *user_data);
+       int (*volume_down)(struct media_player *mp, void *user_data);
 #else
-       int (*fast_forward) (struct media_player *mp, void *user_data);
-       int (*rewind) (struct media_player *mp, void *user_data);
+       int (*fast_forward)(struct media_player *mp, void *user_data);
+       int (*rewind)(struct media_player *mp, void *user_data);
+       int (*press)(struct media_player *mp, uint8_t avc_key,
+                                                       void *user_data);
+       int (*hold)(struct media_player *mp, uint8_t avc_key,
+                                                       void *user_data);
+       int (*release)(struct media_player *mp, void *user_data);
 #endif
-       int (*list_items) (struct media_player *mp, const char *name,
+       int (*list_items)(struct media_player *mp, const char *name,
                                uint32_t start, uint32_t end, void *user_data);
-       int (*change_folder) (struct media_player *mp, const char *path,
+       int (*change_folder)(struct media_player *mp, const char *path,
                                                uint64_t uid, void *user_data);
-       int (*search) (struct media_player *mp, const char *string,
+       int (*search)(struct media_player *mp, const char *string,
                                                void *user_data);
-       int (*play_item) (struct media_player *mp, const char *name,
+       int (*play_item)(struct media_player *mp, const char *name,
                                        uint64_t uid, void *user_data);
-       int (*add_to_nowplaying) (struct media_player *mp, const char *name,
+       int (*add_to_nowplaying)(struct media_player *mp, const char *name,
                                        uint64_t uid, void *user_data);
-       int (*total_items) (struct media_player *mp, const char *name,
+       int (*total_items)(struct media_player *mp, const char *name,
                                                void *user_data);
 };