From e7bd8905653ec2af14ae202f538ebc2646752ce0 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 11 May 2016 12:30:49 -0700 Subject: [PATCH] Implement AVRCP Controller (Client) Browsing. Following is included as part of the features: a) Browsing for all 4 scopes (Media, VFS, Player and Now Playing) b) Player selection and song selection from browse list. The change constructs the following (from lower to upper protocol layers): 1. AVCTP Browse (bta/ & stack/) -- Connection Handling -- Constructing browse commands -- Parsing browse responses 2. AVRCP (btif/) -- JNI interaction/API -- Connection handling to Java -- Delegating request and responses to Java Bug: 28791287 Change-Id: Ibc97ded93cb9c469778ea1e37733390d561cd4cd (cherry picked from commit 9ea8d07c9286a1f4d338dd64ee02266e324d28e5) --- bta/av/bta_av_act.cc | 46 ++- bta/av/bta_av_cfg.cc | 3 +- bta/av/bta_av_main.cc | 62 ++-- bta/include/bta_av_api.h | 60 ++-- btif/src/btif_av.cc | 6 +- btif/src/btif_rc.cc | 823 ++++++++++++++++++++++++++++++++++++++++++++-- include/bt_target.h | 63 ++++ stack/avct/avct_api.c | 4 +- stack/avct/avct_bcb_act.c | 1 - stack/avct/avct_l2c_br.c | 2 - stack/avrc/avrc_bld_ct.c | 139 +++++++- stack/avrc/avrc_int.h | 5 +- stack/avrc/avrc_pars_ct.c | 246 ++++++++++++++ stack/include/avrc_defs.h | 4 +- 14 files changed, 1347 insertions(+), 117 deletions(-) diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc index 38b683a16..66d281bb8 100644 --- a/bta/av/bta_av_act.cc +++ b/bta/av/bta_av_act.cc @@ -535,13 +535,12 @@ void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) return; } - if (p_cb->features & BTA_AV_FEAT_RCTG) - { - /* listen to browsing channel when the connection is open, - * if peer initiated AVRCP connection and local device supports browsing channel */ - if ((p_cb->features & BTA_AV_FEAT_BROWSE) && (p_cb->rcb[i].peer_features == 0)) - AVRC_OpenBrowse(p_data->rc_conn_chg.handle, AVCT_ACP); - } + APPL_TRACE_DEBUG("%s local features %d peer features %d", + __func__, p_cb->features, p_cb->rcb[i].peer_features); + + /* listen to browsing channel when the connection is open, + * if peer initiated AVRCP connection and local device supports browsing channel */ + AVRC_OpenBrowse (p_data->rc_conn_chg.handle, AVCT_ACP); if (p_cb->rcb[i].lidx == (BTA_AV_NUM_LINKS + 1) && shdl != 0) { @@ -588,7 +587,7 @@ void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) bdcpy(rc_open.peer_addr, p_data->rc_conn_chg.peer_addr); rc_open.peer_features = p_cb->rcb[i].peer_features; rc_open.status = BTA_AV_SUCCESS; - APPL_TRACE_DEBUG("local features:x%x peer_features:x%x", p_cb->features, + APPL_TRACE_DEBUG("%s local features:x%x peer_features:x%x", __func__, p_cb->features, rc_open.peer_features); if (rc_open.peer_features == 0) { @@ -604,14 +603,15 @@ void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) (*p_cb->p_cback)(BTA_AV_RC_OPEN_EVT, (tBTA_AV *) &rc_open); /* if local initiated AVRCP connection and both peer and locals device support - * browsing channel, open the browsing channel now */ + * browsing channel, open the browsing channel now + * TODO (sanketa): Some TG would not broadcast browse feature hence check inter-op. */ if ((p_cb->features & BTA_AV_FEAT_BROWSE) && (rc_open.peer_features & BTA_AV_FEAT_BROWSE) && ((p_cb->rcb[i].status & BTA_AV_RC_ROLE_MASK) == BTA_AV_RC_ROLE_INT)) { + APPL_TRACE_DEBUG("%s opening AVRC Browse channel", __func__); AVRC_OpenBrowse (p_data->rc_conn_chg.handle, AVCT_INT); } - } /******************************************************************************* @@ -1143,6 +1143,20 @@ void bta_av_rc_close (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) /******************************************************************************* ** +** Function bta_av_rc_browse_close +** +** Description Empty placeholder. +** +** Returns void +** +*******************************************************************************/ +void bta_av_rc_browse_close (tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) +{ + APPL_TRACE_WARNING("%s empty placeholder does nothing!", __func__); +} + +/******************************************************************************* +** ** Function bta_av_get_shdl ** ** Returns The index to p_scb[] @@ -1473,8 +1487,11 @@ void bta_av_disable(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) * expect BTA_AV_DEREG_COMP_EVT when deregister is complete */ for(xx=0; xxp_scb[xx] != NULL) + { + hdr.layer_specific = xx + 1; + bta_av_api_deregister((tBTA_AV_DATA *)&hdr); + } } alarm_free(p_cb->link_signalling_timer); @@ -1855,7 +1872,7 @@ tBTA_AV_FEAT bta_avk_check_peer_features (uint16_t service_uuid) */ if (peer_rc_version >= AVRC_REV_1_3) { - /* get supported categories */ + /* get supported features */ tSDP_DISC_ATTR *p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES); if (p_attr != NULL) { @@ -1864,6 +1881,8 @@ tBTA_AV_FEAT bta_avk_check_peer_features (uint16_t service_uuid) peer_features |= (BTA_AV_FEAT_ADV_CTRL); if (categories & AVRC_SUPF_CT_APP_SETTINGS) peer_features |= (BTA_AV_FEAT_APP_SETTING); + if (categories & AVRC_SUPF_CT_BROWSE) + peer_features |= (BTA_AV_FEAT_BROWSE); } } } @@ -1930,6 +1949,7 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data) { /* This is Sink + CT + TG(Abs Vol) */ peer_features = bta_avk_check_peer_features(UUID_SERVCLASS_AV_REM_CTRL_TARGET); + APPL_TRACE_DEBUG("%s populating rem ctrl target features %d", __func__, peer_features); if (BTA_AV_FEAT_ADV_CTRL & bta_avk_check_peer_features(UUID_SERVCLASS_AV_REMOTE_CONTROL)) peer_features |= (BTA_AV_FEAT_ADV_CTRL|BTA_AV_FEAT_RCCT); } diff --git a/bta/av/bta_av_cfg.cc b/bta/av/bta_av_cfg.cc index 3113592ea..34f8363d7 100644 --- a/bta/av/bta_av_cfg.cc +++ b/bta/av/bta_av_cfg.cc @@ -124,10 +124,11 @@ const tBTA_AV_CFG bta_av_cfg = BTA_AV_RC_COMP_ID, /* AVRCP Company ID */ #if (AVRC_METADATA_INCLUDED == TRUE) 512, /* AVRCP MTU at L2CAP for control channel */ + BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */ #else 48, /* AVRCP MTU at L2CAP for control channel */ -#endif BTA_AV_MAX_RC_BR_MTU, /* AVRCP MTU at L2CAP for browsing channel */ +#endif BTA_AV_RC_SUPF_CT, /* AVRCP controller categories */ BTA_AV_RC_SUPF_TG, /* AVRCP target categories */ 672, /* AVDTP signaling channel MTU at L2CAP */ diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc index 997be12af..9dd7c2906 100644 --- a/bta/av/bta_av_main.cc +++ b/bta/av/bta_av_main.cc @@ -82,6 +82,7 @@ enum BTA_AV_RC_META_RSP, BTA_AV_RC_MSG, BTA_AV_RC_CLOSE, + BTA_AV_RC_BROWSE_CLOSE, BTA_AV_NUM_ACTIONS }; @@ -103,7 +104,6 @@ const tBTA_AV_ACTION bta_av_action[] = bta_av_rc_meta_rsp, bta_av_rc_msg, bta_av_rc_close, - NULL }; /* state table information */ @@ -114,31 +114,31 @@ const tBTA_AV_ACTION bta_av_action[] = /* state table for init state */ static const uint8_t bta_av_st_init[][BTA_AV_NUM_COLS] = { -/* Event Action 1 Next state */ -/* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, -/* API_REMOTE_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, -/* API_VENDOR_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, -/* API_VENDOR_RSP_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, -/* API_META_RSP_EVT */ {BTA_AV_RC_FREE_RSP, BTA_AV_INIT_ST }, -/* API_RC_CLOSE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, -/* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, -/* AVRC_MSG_EVT */ {BTA_AV_RC_FREE_BROWSE_MSG, BTA_AV_INIT_ST }, -/* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* Event Action 1 Next state */ +/* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, +/* API_REMOTE_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* API_VENDOR_CMD_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* API_VENDOR_RSP_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* API_META_RSP_EVT */ {BTA_AV_RC_FREE_RSP, BTA_AV_INIT_ST }, +/* API_RC_CLOSE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, +/* AVRC_MSG_EVT */ {BTA_AV_RC_FREE_BROWSE_MSG, BTA_AV_INIT_ST }, +/* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, }; /* state table for open state */ static const uint8_t bta_av_st_open[][BTA_AV_NUM_COLS] = { -/* Event Action 1 Next state */ -/* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, -/* API_REMOTE_CMD_EVT */ {BTA_AV_RC_REMOTE_CMD, BTA_AV_OPEN_ST }, -/* API_VENDOR_CMD_EVT */ {BTA_AV_RC_VENDOR_CMD, BTA_AV_OPEN_ST }, -/* API_VENDOR_RSP_EVT */ {BTA_AV_RC_VENDOR_RSP, BTA_AV_OPEN_ST }, -/* API_META_RSP_EVT */ {BTA_AV_RC_META_RSP, BTA_AV_OPEN_ST }, -/* API_RC_CLOSE_EVT */ {BTA_AV_RC_CLOSE, BTA_AV_OPEN_ST }, -/* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, -/* AVRC_MSG_EVT */ {BTA_AV_RC_MSG, BTA_AV_OPEN_ST }, -/* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, +/* Event Action 1 Next state */ +/* API_DISABLE_EVT */ {BTA_AV_DISABLE, BTA_AV_INIT_ST }, +/* API_REMOTE_CMD_EVT */ {BTA_AV_RC_REMOTE_CMD, BTA_AV_OPEN_ST }, +/* API_VENDOR_CMD_EVT */ {BTA_AV_RC_VENDOR_CMD, BTA_AV_OPEN_ST }, +/* API_VENDOR_RSP_EVT */ {BTA_AV_RC_VENDOR_RSP, BTA_AV_OPEN_ST }, +/* API_META_RSP_EVT */ {BTA_AV_RC_META_RSP, BTA_AV_OPEN_ST }, +/* API_RC_CLOSE_EVT */ {BTA_AV_RC_CLOSE, BTA_AV_OPEN_ST }, +/* AVRC_OPEN_EVT */ {BTA_AV_RC_OPENED, BTA_AV_OPEN_ST }, +/* AVRC_MSG_EVT */ {BTA_AV_RC_MSG, BTA_AV_OPEN_ST }, +/* AVRC_NONE_EVT */ {BTA_AV_IGNORE, BTA_AV_INIT_ST }, }; /* type for state table */ @@ -503,6 +503,12 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data) bta_ar_reg_avct(p_bta_av_cfg->avrc_mtu, p_bta_av_cfg->avrc_br_mtu, (uint8_t)(bta_av_cb.sec_mask & (~BTA_SEC_AUTHORIZE)), BTA_ID_AV); #endif + + /* Both Audio Source and Audio Sink support AVRCP 1.6 for the + * major roles (i.e. Audio Source -> TG 1.6 and vice versa). For + * Audio Sink role we support additional TG 1.3 to support + * absolute volume. Here we only do TG registration. + */ uint16_t profile_version = AVRC_REV_1_0; if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) { @@ -659,14 +665,21 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data) /* create an SDP record as AVRC CT. We create 1.3 for SOURCE * because we rely on feature bits being scanned by external * devices more than the profile version itself. + * + * We create 1.4 for SINK since we support browsing. */ - if ((profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) || - (profile_initialized == UUID_SERVCLASS_AUDIO_SINK)) + if (profile_initialized == UUID_SERVCLASS_AUDIO_SOURCE) { bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL, p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV, (bta_av_cb.features & BTA_AV_FEAT_BROWSE), AVRC_REV_1_3); } + else if (profile_initialized == UUID_SERVCLASS_AUDIO_SINK) + { + bta_ar_reg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL, NULL, + p_bta_av_cfg->avrc_ct_cat, BTA_ID_AV, + (bta_av_cb.features & BTA_AV_FEAT_BROWSE), AVRC_REV_1_4); + } #endif } } @@ -1191,11 +1204,12 @@ void bta_av_sm_execute(tBTA_AV_CB *p_cb, uint16_t event, tBTA_AV_DATA *p_data) /* set next state */ p_cb->state = state_table[event][BTA_AV_NEXT_STATE]; - APPL_TRACE_EVENT("next state=%d", p_cb->state); + APPL_TRACE_EVENT("next state=%d event offset:%d", p_cb->state, event); /* execute action functions */ if ((action = state_table[event][BTA_AV_ACTION_COL]) != BTA_AV_IGNORE) { + APPL_TRACE_EVENT("%s action executed %d", __func__, action); (*bta_av_action[action])(p_cb, p_data); } } diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h index f6526215d..35fb4c72d 100644 --- a/bta/include/bta_av_api.h +++ b/bta/include/bta_av_api.h @@ -197,39 +197,37 @@ typedef uint8_t tBTA_AV_CODE; typedef uint8_t tBTA_AV_ERR; - /* AV callback events */ -#define BTA_AV_ENABLE_EVT 0 /* AV enabled */ -#define BTA_AV_REGISTER_EVT 1 /* registered to AVDT */ -#define BTA_AV_OPEN_EVT 2 /* connection opened */ -#define BTA_AV_CLOSE_EVT 3 /* connection closed */ -#define BTA_AV_START_EVT 4 /* stream data transfer started */ -#define BTA_AV_STOP_EVT 5 /* stream data transfer stopped */ -#define BTA_AV_PROTECT_REQ_EVT 6 /* content protection request */ -#define BTA_AV_PROTECT_RSP_EVT 7 /* content protection response */ -#define BTA_AV_RC_OPEN_EVT 8 /* remote control channel open */ -#define BTA_AV_RC_CLOSE_EVT 9 /* remote control channel closed */ -#define BTA_AV_RC_BROWSE_OPEN_EVT 10 /* remote control browse channel open */ -#define BTA_AV_RC_BROWSE_CLOSE_EVT 11 /* remote control browse channel closed */ -#define BTA_AV_REMOTE_CMD_EVT 12 /* remote control command */ -#define BTA_AV_REMOTE_RSP_EVT 13 /* remote control response */ -#define BTA_AV_VENDOR_CMD_EVT 14 /* vendor dependent remote control command */ -#define BTA_AV_VENDOR_RSP_EVT 15 /* vendor dependent remote control response */ -#define BTA_AV_RECONFIG_EVT 16 /* reconfigure response */ -#define BTA_AV_SUSPEND_EVT 17 /* suspend response */ -#define BTA_AV_PENDING_EVT 18 /* incoming connection pending: - * signal channel is open and stream is - * not open after - * BTA_AV_SIGNALLING_TIMEOUT_MS */ -#define BTA_AV_META_MSG_EVT 19 /* metadata messages */ -#define BTA_AV_REJECT_EVT 20 /* incoming connection rejected */ -#define BTA_AV_RC_FEAT_EVT 21 /* remote control channel peer supported features update */ -#define BTA_AV_SINK_MEDIA_CFG_EVT 22 /* command to configure sink codec */ -#define BTA_AV_SINK_MEDIA_DATA_EVT 23 /* sending sink data to Media Task */ -#define BTA_AV_OFFLOAD_START_RSP_EVT 24 /* a2dp offload start response */ +#define BTA_AV_ENABLE_EVT 0 /* AV enabled */ +#define BTA_AV_REGISTER_EVT 1 /* registered to AVDT */ +#define BTA_AV_OPEN_EVT 2 /* connection opened */ +#define BTA_AV_CLOSE_EVT 3 /* connection closed */ +#define BTA_AV_START_EVT 4 /* stream data transfer started */ +#define BTA_AV_STOP_EVT 5 /* stream data transfer stopped */ +#define BTA_AV_PROTECT_REQ_EVT 6 /* content protection request */ +#define BTA_AV_PROTECT_RSP_EVT 7 /* content protection response */ +#define BTA_AV_RC_OPEN_EVT 8 /* remote control channel open */ +#define BTA_AV_RC_CLOSE_EVT 9 /* remote control channel closed */ +#define BTA_AV_REMOTE_CMD_EVT 10 /* remote control command */ +#define BTA_AV_REMOTE_RSP_EVT 11 /* remote control response */ +#define BTA_AV_VENDOR_CMD_EVT 12 /* vendor dependent remote control command */ +#define BTA_AV_VENDOR_RSP_EVT 13 /* vendor dependent remote control response */ +#define BTA_AV_RECONFIG_EVT 14 /* reconfigure response */ +#define BTA_AV_SUSPEND_EVT 15 /* suspend response */ +#define BTA_AV_PENDING_EVT 16 /* incoming connection pending: + * signal channel is open and stream is + * not open after + * BTA_AV_SIGNALLING_TIMEOUT_MS */ +#define BTA_AV_META_MSG_EVT 17 /* metadata messages */ +#define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */ +#define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */ +#define BTA_AV_SINK_MEDIA_CFG_EVT 20 /* command to configure codec */ +#define BTA_AV_SINK_MEDIA_DATA_EVT 21 /* sending data to Media Task */ +#define BTA_AV_OFFLOAD_START_RSP_EVT 22 /* a2dp offload start response */ +#define BTA_AV_RC_BROWSE_OPEN_EVT 23 /* remote control channel open */ +#define BTA_AV_RC_BROWSE_CLOSE_EVT 24 /* remote control channel closed */ /* Max BTA event */ -#define BTA_AV_MAX_EVT 25 - +#define BTA_AV_MAX_EVT 25 typedef uint8_t tBTA_AV_EVT; diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc index 13c50a2fd..297f96f64 100644 --- a/btif/src/btif_av.cc +++ b/btif/src/btif_av.cc @@ -116,8 +116,8 @@ else\ /* Helper macro to avoid code duplication in the state machine handlers */ #define CHECK_RC_EVENT(e, d) \ case BTA_AV_RC_OPEN_EVT: \ - case BTA_AV_RC_CLOSE_EVT: \ case BTA_AV_RC_BROWSE_OPEN_EVT: \ + case BTA_AV_RC_CLOSE_EVT: \ case BTA_AV_RC_BROWSE_CLOSE_EVT: \ case BTA_AV_REMOTE_CMD_EVT: \ case BTA_AV_VENDOR_CMD_EVT: \ @@ -1074,12 +1074,12 @@ static void btif_av_handle_event(uint16_t event, char* p_param) void btif_av_event_deep_copy(uint16_t event, char *p_dest, char *p_src) { + BTIF_TRACE_DEBUG("%s", __func__); tBTA_AV *av_src = (tBTA_AV *)p_src; tBTA_AV *av_dest = (tBTA_AV *)p_dest; // First copy the structure maybe_non_aligned_memcpy(av_dest, av_src, sizeof(*av_src)); - switch (event) { case BTA_AV_META_MSG_EVT: @@ -1594,7 +1594,7 @@ bt_status_t btif_av_sink_execute_service(bool b_enable) * be initiated by the app/audioflinger layers */ BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_NO_SCO_SSPD|BTA_AV_FEAT_RCCT| BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR| - BTA_AV_FEAT_ADV_CTRL|BTA_AV_FEAT_RCTG, + BTA_AV_FEAT_ADV_CTRL|BTA_AV_FEAT_RCTG|BTA_AV_FEAT_BROWSE, bte_av_callback); BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AVK_SERVICE_NAME, 0, bte_av_sink_media_callback, UUID_SERVCLASS_AUDIO_SINK); diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc index e3b6314a1..d287fe1d8 100644 --- a/btif/src/btif_rc.cc +++ b/btif/src/btif_rc.cc @@ -97,6 +97,16 @@ do { \ } \ } while (0) +#define CHECK_BR_CONNECTED(p_dev) \ +do { \ + BTIF_TRACE_DEBUG("## %s ##", __FUNCTION__); \ + if (p_dev == NULL || p_dev->br_connected == false) \ + { \ + BTIF_TRACE_WARNING("Function %s() called when BR is not connected", __FUNCTION__); \ + return BT_STATUS_NOT_READY; \ + } \ +} while (0) + /***************************************************************************** ** Local type definitions ******************************************************************************/ @@ -165,6 +175,7 @@ typedef struct { /* TODO : Merge btif_rc_reg_notifications_t and btif_rc_cmd_ctxt_t to a single struct */ typedef struct { bool rc_connected; + bool br_connected; // Browsing channel. uint8_t rc_handle; tBTA_AV_FEAT rc_features; btrc_connection_state_t rc_state; @@ -284,6 +295,7 @@ static rc_transaction_t* get_transaction_by_lbl(uint8_t label); #if (AVRC_ADV_CTRL_INCLUDED == TRUE) static void handle_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg, btif_rc_device_cb_t *p_dev); #endif + #if (AVRC_CTRL_INCLUDED == TRUE) static void handle_avk_rc_metamsg_cmd(tBTA_AV_META_MSG *pmeta_msg); static void handle_avk_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg); @@ -324,10 +336,16 @@ static bt_status_t list_player_app_setting_value_cmd(uint8_t attrib_id, btif_rc_device_cb_t *p_dev); static bt_status_t get_player_app_setting_cmd(uint8_t num_attrib, uint8_t* attrib_ids, btif_rc_device_cb_t *p_dev); +void get_folder_item_type_media(const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item); +void get_folder_item_type_folder(const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item); +void get_folder_item_type_player(const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item); +static bt_status_t get_folder_items_cmd(bt_bdaddr_t *bd_addr, uint8_t scope, uint8_t start_item, + uint8_t num_items); #endif static void btif_rc_upstreams_evt(uint16_t event, tAVRC_COMMAND* p_param, uint8_t ctype, uint8_t label, btif_rc_device_cb_t *p_dev); + #if (AVRC_ADV_CTRL_INCLUDED == TRUE) static void btif_rc_upstreams_rsp_evt(uint16_t event, tAVRC_RESPONSE *pavrc_resp, uint8_t ctype, uint8_t label, btif_rc_device_cb_t *p_dev); @@ -583,9 +601,14 @@ void handle_rc_ctrl_features(btif_rc_device_cb_t *p_dev) if (btif_av_is_sink_enabled()) getcapabilities_cmd (AVRC_CAP_COMPANY_ID, p_dev); } + + /* Add browsing feature capability */ + if (p_dev->rc_features & BTA_AV_FEAT_BROWSE) { + rc_features |= BTRC_FEAT_BROWSE; + } + BTIF_TRACE_DEBUG("%s: Update rc features to CTRL: %d", __func__, rc_features); HAL_CBACK(bt_rc_ctrl_callbacks, getrcfeatures_cb, &rc_addr, rc_features); - } #endif @@ -673,6 +696,37 @@ void handle_rc_features(btif_rc_device_cb_t *p_dev) /*************************************************************************** * Function handle_rc_connect * + * - Argument: tBTA_AV_RC_OPEN browse RC open data structure + * + * - Description: browse RC connection event handler + * + ***************************************************************************/ +void handle_rc_browse_connect(tBTA_AV_RC_OPEN *p_rc_open) { + BTIF_TRACE_DEBUG("%s rc_handle %d status %d", + __func__, p_rc_open->rc_handle, p_rc_open->status); +#if (AVRC_CTRL_INCLUDED == TRUE) + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_handle(p_rc_open->rc_handle); + + if (!p_dev) { + BTIF_TRACE_ERROR("%s p_dev is null", __func__); + return; + } + + /* check that we are already connected to this address since being connected + * to a browse when not connected to the control channel over AVRCP is + * probably not preferred anyways. */ + if (p_rc_open->status == BTA_AV_SUCCESS) { + bt_bdaddr_t rc_addr; + bdcpy(rc_addr.address, p_dev->rc_addr); + p_dev->br_connected = true; + HAL_CBACK(bt_rc_ctrl_callbacks, connection_state_cb, true, true, &rc_addr); + } +#endif +} + +/*************************************************************************** + * Function handle_rc_connect + * * - Argument: tBTA_AV_RC_OPEN RC open data structure * * - Description: RC connection event handler @@ -746,7 +800,7 @@ void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open) bdcpy(rc_addr.address, p_dev->rc_addr); if (bt_rc_ctrl_callbacks != NULL) { - HAL_CBACK(bt_rc_ctrl_callbacks, connection_state_cb, true, &rc_addr); + HAL_CBACK(bt_rc_ctrl_callbacks, connection_state_cb, true, false, &rc_addr); } /* report connection state if remote device is AVRCP target */ handle_rc_ctrl_features(p_dev); @@ -827,7 +881,7 @@ void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close) /* report connection state if device is AVRCP target */ if (bt_rc_ctrl_callbacks != NULL) { - HAL_CBACK(bt_rc_ctrl_callbacks, connection_state_cb, false, &rc_addr); + HAL_CBACK(bt_rc_ctrl_callbacks, connection_state_cb, false, false, &rc_addr); } #endif } @@ -950,8 +1004,8 @@ void handle_rc_passthrough_rsp ( tBTA_AV_REMOTE_RSP *p_remote_rsp) release_transaction(p_remote_rsp->label); if (bt_rc_ctrl_callbacks != NULL) { - HAL_CBACK(bt_rc_ctrl_callbacks, passthrough_rsp_cb, p_remote_rsp->rc_id, key_state, \ - &rc_addr); + HAL_CBACK(bt_rc_ctrl_callbacks, passthrough_rsp_cb, &rc_addr, + p_remote_rsp->rc_id, key_state); } } else @@ -1176,14 +1230,16 @@ void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) handle_rc_connect( &(p_data->rc_open) ); }break; - case BTA_AV_RC_CLOSE_EVT: + case BTA_AV_RC_BROWSE_OPEN_EVT: { - handle_rc_disconnect(&(p_data->rc_close)); + /* tell the UL that we have connection to browse channel and that + * browse commands can be directed accordingly. */ + handle_rc_browse_connect(&(p_data->rc_open)); }break; - case BTA_AV_RC_BROWSE_OPEN_EVT: + case BTA_AV_RC_CLOSE_EVT: { - BTIF_TRACE_DEBUG("%s: BTA_AV_RC_BROWSE_OPEN_EVT", __func__); + handle_rc_disconnect(&(p_data->rc_close)); }break; case BTA_AV_RC_BROWSE_CLOSE_EVT: @@ -1269,26 +1325,43 @@ void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) else if ((bt_rc_callbacks == NULL)&&(bt_rc_ctrl_callbacks != NULL)) { /* This is case of Sink + CT + TG(for abs vol)) */ - BTIF_TRACE_DEBUG("%s: BTA_AV_META_MSG_EVT code: %d label: %d", - __func__, - p_data->meta_msg.code, - p_data->meta_msg.label); - BTIF_TRACE_DEBUG("%s: company_id: 0x%x len: %d handle: %d", - __func__, + BTIF_TRACE_DEBUG( + "%s BTA_AV_META_MSG_EVT code:%d label:%d opcode %d ctype %d", + __FUNCTION__, + p_data->meta_msg.code, + p_data->meta_msg.label, + p_data->meta_msg.p_msg->hdr.opcode, + p_data->meta_msg.p_msg->hdr.ctype); + BTIF_TRACE_DEBUG("%s company_id:0x%x len:%d handle:%d", + __FUNCTION__, p_data->meta_msg.company_id, p_data->meta_msg.len, p_data->meta_msg.rc_handle); + switch (p_data->meta_msg.p_msg->hdr.opcode) { + case AVRC_OP_VENDOR: + if ((p_data->meta_msg.code >= AVRC_RSP_NOT_IMPL)&& + (p_data->meta_msg.code <= AVRC_RSP_INTERIM)) + { + /* Its a response */ + handle_avk_rc_metamsg_rsp(&(p_data->meta_msg)); + } + else if (p_data->meta_msg.code <= AVRC_CMD_GEN_INQ) + { + /* Its a command */ + handle_avk_rc_metamsg_cmd(&(p_data->meta_msg)); + } + break; - if ((p_data->meta_msg.code >= AVRC_RSP_NOT_IMPL)&& - (p_data->meta_msg.code <= AVRC_RSP_INTERIM)) - { - /* Its a response */ - handle_avk_rc_metamsg_rsp(&(p_data->meta_msg)); - } - else if (p_data->meta_msg.code <= AVRC_CMD_GEN_INQ) - { - /* Its a command */ - handle_avk_rc_metamsg_cmd(&(p_data->meta_msg)); + case AVRC_OP_BROWSE: + if (p_data->meta_msg.p_msg->hdr.ctype == AVRC_CMD) + { + handle_avk_rc_metamsg_cmd(&(p_data->meta_msg)); + } + else if (p_data->meta_msg.p_msg->hdr.ctype == AVRC_RSP) + { + handle_avk_rc_metamsg_rsp(&(p_data->meta_msg)); + } + break; } } @@ -2094,6 +2167,7 @@ static bt_status_t get_play_status_rsp(bt_bdaddr_t *bd_addr, btrc_play_status_t memset(&(avrc_rsp.get_play_status), 0, sizeof(tAVRC_GET_PLAY_STATUS_RSP)); + BTIF_TRACE_DEBUG("%s song len %d song pos %d", __func__, song_len, song_pos); avrc_rsp.get_play_status.song_len = song_len; avrc_rsp.get_play_status.song_pos = song_pos; avrc_rsp.get_play_status.play_status = play_status; @@ -4283,6 +4357,313 @@ static void handle_get_playstatus_response (tBTA_AV_META_MSG *pmeta_msg, /*************************************************************************** ** +** Function handle_get_folder_items_response +** +** Description handles the the get folder items response, calls +** HAL callback to send the folder items. +** Returns None +** +***************************************************************************/ +static void handle_get_folder_items_response ( + tBTA_AV_META_MSG *pmeta_msg, tAVRC_GET_ITEMS_RSP *p_rsp) +{ + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_handle(pmeta_msg->rc_handle); + bt_bdaddr_t rc_addr; + bdcpy(rc_addr.address, p_dev->rc_addr); + + if (p_rsp->status == AVRC_STS_NO_ERROR) + { + /* Convert the internal folder listing into a response that can + * be passed onto JNI via HAL_CBACK + */ + uint8_t item_count = p_rsp->item_count; + btrc_folder_items_t *btrc_items = + (btrc_folder_items_t *) osi_malloc(sizeof(btrc_folder_items_t) * item_count); + for (uint8_t i = 0; i < item_count; i++) { + const tAVRC_ITEM *avrc_item = &(p_rsp->p_item_list[i]); + btrc_folder_items_t *btrc_item = &(btrc_items[i]); + BTIF_TRACE_DEBUG("%s folder item type %d", __func__, avrc_item->item_type); + switch (avrc_item->item_type) { + case AVRC_ITEM_MEDIA: + BTIF_TRACE_DEBUG("%s setting type to %d", __func__, BTRC_ITEM_MEDIA); + get_folder_item_type_media(avrc_item, btrc_item); + break; + + case AVRC_ITEM_FOLDER: + BTIF_TRACE_DEBUG("%s setting type to BTRC_ITEM_FOLDER", __func__); + get_folder_item_type_folder(avrc_item, btrc_item); + break; + + case AVRC_ITEM_PLAYER: + BTIF_TRACE_DEBUG("%s setting type to BTRC_ITEM_PLAYER", __func__); + get_folder_item_type_player(avrc_item, btrc_item); + break; + + default: + BTIF_TRACE_ERROR("%s cannot understand folder item type %d", + __func__, avrc_item->item_type); + } + } + + HAL_CBACK(bt_rc_ctrl_callbacks, + get_folder_items_cb, + &rc_addr, + /* We want to make the ownership explicit in native */ + (const btrc_folder_items_t *) btrc_items, + item_count); + BTIF_TRACE_DEBUG("%s HAL CBACK get_folder_items_cb finished", __func__); + + /* Release the memory block for items since we OWN the object */ + osi_free(btrc_items); + } + else + { + BTIF_TRACE_ERROR("%s: Error %d", __func__, p_rsp->status); + } +} + +/*************************************************************************** +** +** Function get_folder_item_type_media +** +** Description Converts the AVRC representation of a folder item with +** TYPE media to BTIF representation. +** Returns None +** +***************************************************************************/ +void get_folder_item_type_media( + const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item) +{ + btrc_item->item_type = BTRC_ITEM_MEDIA; + const tAVRC_ITEM_MEDIA *avrc_item_media = &(avrc_item->u.media); + btrc_item_media_t *btrc_item_media = &(btrc_item->media); + /* UID */ + memset(btrc_item_media->uid, 0, BTRC_UID_SIZE * sizeof (uint8_t)); + memcpy(btrc_item_media->uid, + avrc_item_media->uid, + sizeof(uint8_t) * BTRC_UID_SIZE); + + /* Audio/Video type */ + switch (avrc_item_media->type) { + case AVRC_MEDIA_TYPE_AUDIO: + btrc_item_media->type = BTRC_MEDIA_TYPE_AUDIO; + break; + case AVRC_MEDIA_TYPE_VIDEO: + btrc_item_media->type = BTRC_MEDIA_TYPE_VIDEO; + break; + } + + /* Charset ID */ + btrc_item_media->charset_id = avrc_item_media->name.charset_id; + + /* Copy the name */ + BTIF_TRACE_DEBUG("%s max len %d str len %d", __func__, BTRC_MAX_ATTR_STR_LEN, + avrc_item_media->name.str_len); + memset(btrc_item_media->name, 0, BTRC_MAX_ATTR_STR_LEN * sizeof (uint8_t)); + memcpy(btrc_item_media->name, + avrc_item_media->name.p_str, + sizeof(uint8_t) * (avrc_item_media->name.str_len)); + + /* Copy the parameters */ + btrc_item_media->num_attrs = avrc_item_media->attr_count; + btrc_item_media->p_attrs = (btrc_element_attr_val_t *) osi_malloc ( + btrc_item_media->num_attrs * sizeof (btrc_element_attr_val_t)); + + /* Extract each attribute */ + for (int i = 0; i < avrc_item_media->attr_count; i++) { + btrc_element_attr_val_t *btrc_attr_pair = + &(btrc_item_media->p_attrs[i]); + tAVRC_ATTR_ENTRY *avrc_attr_pair = + &(avrc_item_media->p_attr_list[i]); + + BTIF_TRACE_DEBUG("%s media attr id 0x%x", __func__, + avrc_attr_pair->attr_id); + + switch (avrc_attr_pair->attr_id) { + case AVRC_MEDIA_ATTR_ID_TITLE: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_TITLE; + break; + case AVRC_MEDIA_ATTR_ID_ARTIST: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_ARTIST; + break; + case AVRC_MEDIA_ATTR_ID_ALBUM: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_ALBUM; + break; + case AVRC_MEDIA_ATTR_ID_TRACK_NUM: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_TRACK_NUM; + break; + case AVRC_MEDIA_ATTR_ID_NUM_TRACKS: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_NUM_TRACKS; + break; + case AVRC_MEDIA_ATTR_ID_GENRE: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_GENRE; + break; + case AVRC_MEDIA_ATTR_ID_PLAYING_TIME: + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_PLAYING_TIME; + break; + default: + BTIF_TRACE_ERROR("%s invalid media attr id: 0x%x", + __func__, avrc_attr_pair->attr_id); + btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_INVALID; + } + + memset(btrc_attr_pair->text, 0, + BTRC_MAX_ATTR_STR_LEN * sizeof (uint8_t)); + memcpy(btrc_attr_pair->text, + avrc_attr_pair->name.p_str, + avrc_attr_pair->name.str_len); + } +} + +/*************************************************************************** +** +** Function get_folder_item_type_folder +** +** Description Converts the AVRC representation of a folder item with +** TYPE folder to BTIF representation. +** Returns None +** +***************************************************************************/ +void get_folder_item_type_folder( + const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item) +{ + btrc_item->item_type = BTRC_ITEM_FOLDER; + const tAVRC_ITEM_FOLDER *avrc_item_folder = &(avrc_item->u.folder); + btrc_item_folder_t *btrc_item_folder = &(btrc_item->folder); + /* Copy the UID */ + memset(btrc_item_folder->uid, 0, BTRC_UID_SIZE * sizeof (uint8_t)); + memcpy(btrc_item_folder->uid, avrc_item_folder->uid, sizeof(uint8_t) * BTRC_UID_SIZE); + + /* Copy the type */ + switch (avrc_item_folder->type) { + case AVRC_FOLDER_TYPE_MIXED: + btrc_item_folder->type = BTRC_FOLDER_TYPE_MIXED; + break; + case AVRC_FOLDER_TYPE_TITLES: + btrc_item_folder->type = BTRC_FOLDER_TYPE_TITLES; + break; + case AVRC_FOLDER_TYPE_ALNUMS: + btrc_item_folder->type = BTRC_FOLDER_TYPE_ALBUMS; + break; + case AVRC_FOLDER_TYPE_ARTISTS: + btrc_item_folder->type = BTRC_FOLDER_TYPE_ARTISTS; + break; + case AVRC_FOLDER_TYPE_GENRES: + btrc_item_folder->type = BTRC_FOLDER_TYPE_GENRES; + break; + case AVRC_FOLDER_TYPE_PLAYLISTS: + btrc_item_folder->type = BTRC_FOLDER_TYPE_PLAYLISTS; + break; + case AVRC_FOLDER_TYPE_YEARS: + btrc_item_folder->type = BTRC_FOLDER_TYPE_YEARS; + break; + } + + /* Copy if playable */ + btrc_item_folder->playable = avrc_item_folder->playable; + + /* Copy name */ + BTIF_TRACE_DEBUG("%s max len %d str len %d", __func__, BTRC_MAX_ATTR_STR_LEN, + avrc_item_folder->name.str_len); + memset(btrc_item_folder->name, 0, BTRC_MAX_ATTR_STR_LEN * sizeof (uint8_t)); + memcpy(btrc_item_folder->name, + avrc_item_folder->name.p_str, + avrc_item_folder->name.str_len * sizeof (uint8_t)); + + /* Copy charset */ + btrc_item_folder->charset_id = avrc_item_folder->name.charset_id; +} + +/*************************************************************************** +** +** Function get_folder_item_type_player +** +** Description Converts the AVRC representation of a folder item with +** TYPE player to BTIF representation. +** Returns None +** +***************************************************************************/ +void get_folder_item_type_player( + const tAVRC_ITEM *avrc_item, btrc_folder_items_t *btrc_item) +{ + btrc_item->item_type = BTRC_ITEM_PLAYER; + const tAVRC_ITEM_PLAYER *avrc_item_player = &(avrc_item->u.player); + btrc_item_player_t *btrc_item_player = &(btrc_item->player); + /* Player ID */ + btrc_item_player->player_id = avrc_item_player->player_id; + /* Major type */ + btrc_item_player->major_type = avrc_item_player->major_type; + /* Sub type */ + btrc_item_player->sub_type = avrc_item_player->sub_type; + /* Features */ + memcpy(btrc_item_player->features, avrc_item_player->features, + BTRC_FEATURE_BIT_MASK_SIZE); + + memset(btrc_item_player->name, 0, + BTRC_MAX_ATTR_STR_LEN * sizeof(uint8_t)); + memcpy(btrc_item_player->name, + avrc_item_player->name.p_str, + avrc_item_player->name.str_len); +} + +/*************************************************************************** +** +** Function handle_change_path_response +** +** Description handles the the change path response, calls +** HAL callback to send the updated folder +** Returns None +** +***************************************************************************/ +static void handle_change_path_response ( + tBTA_AV_META_MSG *pmeta_msg, tAVRC_CHG_PATH_RSP *p_rsp) +{ + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_handle(pmeta_msg->rc_handle); + bt_bdaddr_t rc_addr; + bdcpy(rc_addr.address, p_dev->rc_addr); + + if (p_rsp->status == AVRC_STS_NO_ERROR) + { + HAL_CBACK(bt_rc_ctrl_callbacks, change_folder_path_cb, + &rc_addr, p_rsp->num_items); + } + else + { + BTIF_TRACE_ERROR("%s error in handle_change_path_response %d", + __func__, p_rsp->status); + } +} + +/*************************************************************************** +** +** Function handle_set_browsed_player_response +** +** Description handles the the change path response, calls +** HAL callback to send the updated folder +** Returns None +** +***************************************************************************/ +static void handle_set_browsed_player_response( + tBTA_AV_META_MSG *pmeta_msg, tAVRC_SET_BR_PLAYER_RSP *p_rsp) +{ + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_handle(pmeta_msg->rc_handle); + bt_bdaddr_t rc_addr; + bdcpy(rc_addr.address, p_dev->rc_addr); + + if (p_rsp->status == AVRC_STS_NO_ERROR) + { + HAL_CBACK(bt_rc_ctrl_callbacks, set_browsed_player_cb, + &rc_addr, p_rsp->num_items, p_rsp->folder_depth); + } + else + { + BTIF_TRACE_ERROR("%s error %d", __func__, p_rsp->status); + } +} + + +/*************************************************************************** +** ** Function clear_cmd_timeout ** ** Description helper function to stop the command timeout timer @@ -4323,13 +4704,13 @@ static void handle_avk_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg) BTIF_TRACE_DEBUG("%s: opcode: %d rsp_code: %d ", __func__, pmeta_msg->p_msg->hdr.opcode, pmeta_msg->code); + status = AVRC_Ctrl_ParsResponse(pmeta_msg->p_msg, &avrc_response, scratch_buf, &buf_len); if ((AVRC_OP_VENDOR == pmeta_msg->p_msg->hdr.opcode)&& (pmeta_msg->code >= AVRC_RSP_NOT_IMPL)&& (pmeta_msg->code <= AVRC_RSP_INTERIM)) { - status = AVRC_Ctrl_ParsResponse(pmeta_msg->p_msg, &avrc_response, scratch_buf, &buf_len); - BTIF_TRACE_DEBUG("%s: parse status: %d pdu: %d rsp_status: %d", - __func__, status, avrc_response.pdu, + BTIF_TRACE_DEBUG("%s parse status %d pdu = %d rsp_status = %d", + __FUNCTION__, status, avrc_response.pdu, pmeta_msg->p_msg->vendor.hdr.ctype); switch (avrc_response.pdu) @@ -4380,7 +4761,25 @@ static void handle_avk_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg) handle_get_playstatus_response(pmeta_msg, &avrc_response.get_play_status); break; } - release_transaction(pmeta_msg->label); + } + else if (AVRC_OP_BROWSE == pmeta_msg->p_msg->hdr.opcode) + { + BTIF_TRACE_DEBUG("%s AVRC_OP_BROWSE pdu %d", __func__, avrc_response.pdu); + /* check what kind of command it is for browsing */ + switch (avrc_response.pdu) { + case AVRC_PDU_GET_FOLDER_ITEMS: + handle_get_folder_items_response(pmeta_msg, &avrc_response.get_items); + break; + case AVRC_PDU_CHANGE_PATH: + handle_change_path_response(pmeta_msg, &avrc_response.chg_path); + break; + case AVRC_PDU_SET_BROWSED_PLAYER: + handle_set_browsed_player_response(pmeta_msg, &avrc_response.br_player); + break; + default: + BTIF_TRACE_ERROR("%s cannot handle browse pdu %d", __func__, + pmeta_msg->p_msg->hdr.opcode); + } } else { @@ -4388,6 +4787,8 @@ static void handle_avk_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg) __func__, pmeta_msg->code, pmeta_msg->len); return; } + BTIF_TRACE_DEBUG("XX __func__ release transaction %d", pmeta_msg->label); + release_transaction(pmeta_msg->label); } /*************************************************************************** @@ -4407,12 +4808,12 @@ static void handle_avk_rc_metamsg_cmd(tBTA_AV_META_MSG *pmeta_msg) BTIF_TRACE_DEBUG("%s: opcode: %d rsp_code: %d",__func__, pmeta_msg->p_msg->hdr.opcode,pmeta_msg->code); + status = AVRC_Ctrl_ParsCommand(pmeta_msg->p_msg, &avrc_cmd); if ((AVRC_OP_VENDOR==pmeta_msg->p_msg->hdr.opcode)&& (pmeta_msg->code <= AVRC_CMD_GEN_INQ)) { - status = AVRC_Ctrl_ParsCommand(pmeta_msg->p_msg, &avrc_cmd); - BTIF_TRACE_DEBUG("%s: Received vendor command.code: %d, PDU: %d label: %d", - __func__, pmeta_msg->code, avrc_cmd.pdu, pmeta_msg->label); + BTIF_TRACE_DEBUG("%s Received vendor command.code %d, PDU %d label %d", + __FUNCTION__, pmeta_msg->code, avrc_cmd.pdu, pmeta_msg->label); if (status != AVRC_STS_NO_ERROR) { @@ -4720,6 +5121,301 @@ static bt_status_t get_player_app_setting_cmd(uint8_t num_attrib, uint8_t* attri /*************************************************************************** ** +** Function get_now_playing_list_cmd +** +** Description Fetch the now playing list +** +** Paramters start_item: First item to fetch (0 to fetch from beganning) +** end_item: Last item to fetch (0xff to fetch until end) +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t get_now_playing_list_cmd(bt_bdaddr_t *bd_addr, uint8_t start_item, + uint8_t num_items) { + BTIF_TRACE_DEBUG("%s start, end: (%d, %d)", __func__, start_item, num_items); +#if (AVRC_CTRL_INCLUDED == TRUE) + return get_folder_items_cmd(bd_addr, AVRC_SCOPE_NOW_PLAYING, start_item, num_items); +#else + BTIF_TRACE_ERROR("%s AVRCP controller role is not enabled", __func__); + return BT_STATUS_FAIL; +#endif +} + +/*************************************************************************** +** +** Function get_folder_list_cmd +** +** Description Fetch the currently selected folder list +** +** Paramters start_item: First item to fetch (0 to fetch from beganning) +** end_item: Last item to fetch (0xff to fetch until end) +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t get_folder_list_cmd(bt_bdaddr_t *bd_addr, uint8_t start_item, + uint8_t num_items) { + BTIF_TRACE_DEBUG("%s start, end: (%d, %d)", __func__, start_item, num_items); +#if (AVRC_CTRL_INCLUDED == TRUE) + return get_folder_items_cmd(bd_addr, AVRC_SCOPE_FILE_SYSTEM, start_item, num_items); +#else + BTIF_TRACE_ERROR("%s AVRCP controller role is not enabled", __func__); + return BT_STATUS_FAIL; +#endif +} + +/*************************************************************************** +** +** Function get_player_list_cmd +** +** Description Fetch the player list +** +** Paramters start_item: First item to fetch (0 to fetch from beganning) +** end_item: Last item to fetch (0xff to fetch until end) +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t get_player_list_cmd(bt_bdaddr_t *bd_addr, uint8_t start_item, + uint8_t num_items) { + BTIF_TRACE_DEBUG("%s start, end: (%d, %d)", __func__, start_item, num_items); +#if (AVRC_CTRL_INCLUDED == TRUE) + return get_folder_items_cmd(bd_addr, AVRC_SCOPE_PLAYER_LIST, start_item, num_items); +#else + BTIF_TRACE_ERROR("%s AVRCP controller role is not enabled", __func__); + return BT_STATUS_FAIL; +#endif +} + +/*************************************************************************** +** +** Function change_folder_path_cmd +** +** Description Change the folder. +** +** Paramters direction: Direction (Up/Down) to change folder +** uid: The UID of folder to move to +** start_item: First item to fetch (0 to fetch from beganning) +** end_item: Last item to fetch (0xff to fetch until end) +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t change_folder_path_cmd(bt_bdaddr_t *bd_addr, uint8_t direction, uint8_t *uid) +{ + BTIF_TRACE_DEBUG("%s direction (%d)", __func__, direction); +#if (AVRC_CTRL_INCLUDED == TRUE) + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_bda(bd_addr); + + CHECK_RC_CONNECTED(p_dev); + CHECK_BR_CONNECTED(p_dev); + + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + rc_transaction_t *p_transaction = NULL; + + if (p_dev->br_connected) + { + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.chg_path.pdu = AVRC_PDU_CHANGE_PATH; + avrc_cmd.chg_path.status = AVRC_STS_NO_ERROR; + // TODO(sanketa): Improve for database aware clients. + avrc_cmd.chg_path.uid_counter = 0; + avrc_cmd.chg_path.direction = direction; + + memset(avrc_cmd.chg_path.folder_uid, 0, AVRC_UID_SIZE * sizeof (uint8_t)); + memcpy(avrc_cmd.chg_path.folder_uid, uid, AVRC_UID_SIZE * sizeof (uint8_t)); + + if (AVRC_BldCommand(&avrc_cmd, &p_msg) == AVRC_STS_NO_ERROR) + { + bt_status_t tran_status = get_transaction(&p_transaction); + if (BT_STATUS_SUCCESS == tran_status && p_transaction != NULL) + { + BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", + __func__, p_transaction->lbl); + BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } + else + { + osi_free(p_msg); + BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x", + __func__, tran_status); + status = BT_STATUS_FAIL; + } + } + else + { + BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status); + status = BT_STATUS_FAIL; + } + } + else + { + BTIF_TRACE_ERROR("%s command not supported by peer features %d", + __func__, p_dev->rc_features); + status = BT_STATUS_FAIL; + } + return (bt_status_t) status; +#else + BTIF_TRACE_ERROR("%s AVRCP controller role is not enabled", __func__); + return BT_STATUS_FAIL; +#endif +} + +/*************************************************************************** +** +** Function set_browsed_player_cmd +** +** Description Change the browsed player. +** +** Paramters id: The UID of player to move to +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t set_browsed_player_cmd(bt_bdaddr_t *bd_addr, uint16_t id) +{ + BTIF_TRACE_DEBUG("%s id (%d)", __func__, id); +#if (AVRC_CTRL_INCLUDED == TRUE) + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_bda(bd_addr); + CHECK_RC_CONNECTED(p_dev); + CHECK_BR_CONNECTED(p_dev); + + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + rc_transaction_t *p_transaction = NULL; + + if (p_dev->br_connected) + { + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.br_player.pdu = AVRC_PDU_SET_BROWSED_PLAYER; + avrc_cmd.br_player.status = AVRC_STS_NO_ERROR; + // TODO(sanketa): Improve for database aware clients. + avrc_cmd.br_player.player_id = id; + + if (AVRC_BldCommand(&avrc_cmd, &p_msg) == AVRC_STS_NO_ERROR) + { + bt_status_t tran_status = get_transaction(&p_transaction); + if (BT_STATUS_SUCCESS == tran_status && p_transaction != NULL) + { + BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", + __func__, p_transaction->lbl); + BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } + else + { + osi_free(p_msg); + BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x", + __func__, tran_status); + status = BT_STATUS_FAIL; + } + } + else + { + BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status); + status = BT_STATUS_FAIL; + } + } + else + { + BTIF_TRACE_ERROR("%s command not supported by peer features %d", + __func__, p_dev->rc_features); + status = BT_STATUS_FAIL; + } + return (bt_status_t) status; +#else + BTIF_TRACE_ERROR("%s AVRCP controller role is not enabled", __func__); + return BT_STATUS_FAIL; +#endif +} + +#if (AVRC_CTRL_INCLUDED == TRUE) +/*************************************************************************** +** +** Function get_folder_items_cmd +** +** Description Helper function to browse the content hierarchy of the +** TG device. +** +** Paramters scope: AVRC_SCOPE_NOW_PLAYING (etc) for various browseable +** content +** start_item: First item to fetch (0 to fetch from beganning) +** end_item: Last item to fetch (0xff to fetch until end) +** +** Returns BT_STATUS_SUCCESS if command issued successfully otherwise +** BT_STATUS_FAIL. +** +***************************************************************************/ +static bt_status_t get_folder_items_cmd(bt_bdaddr_t *bd_addr, uint8_t scope, uint8_t start_item, + uint8_t num_items) +{ + BTIF_TRACE_DEBUG("%s", __func__); + /* Check that both avrcp and browse channel are connected. */ + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_bda(bd_addr); + CHECK_RC_CONNECTED(p_dev); + CHECK_BR_CONNECTED(p_dev); + + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + rc_transaction_t *p_transaction=NULL; + + if (p_dev->br_connected) { + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + /* Set the layer specific to point to browse although this should really + * be done by lower layers and looking at the PDU + */ + avrc_cmd.get_items.pdu = AVRC_PDU_GET_FOLDER_ITEMS; + avrc_cmd.get_items.status = AVRC_STS_NO_ERROR; + avrc_cmd.get_items.scope = scope; + avrc_cmd.get_items.start_item = start_item; + avrc_cmd.get_items.end_item = (start_item + num_items - 1); + avrc_cmd.get_items.attr_count = 0; /* p_attr_list does not matter hence */ + + if (AVRC_BldCommand(&avrc_cmd, &p_msg) == AVRC_STS_NO_ERROR) + { + bt_status_t tran_status = get_transaction(&p_transaction); + if (BT_STATUS_SUCCESS == tran_status && p_transaction != NULL) + { + BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", + __func__, p_transaction->lbl); + BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } + else + { + osi_free(p_msg); + BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x", + __FUNCTION__, tran_status); + status = BT_STATUS_FAIL; + } + } + else + { + BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status); + status = BT_STATUS_FAIL; + } + } else { + BTIF_TRACE_ERROR("%s command not supported by peer features %d", + __func__, p_dev->rc_features); + status = BT_STATUS_FAIL; + } + return (bt_status_t) status; +} +#endif + +/*************************************************************************** +** ** Function change_player_app_setting ** ** Description Set current values of Player Attributes @@ -4782,6 +5478,62 @@ static bt_status_t change_player_app_setting(bt_bdaddr_t *bd_addr, uint8_t num_a /*************************************************************************** ** +** Function play_item_cmd +** +** Description Play the item specified by UID & scope +** +** Returns void +** +***************************************************************************/ +static bt_status_t play_item_cmd( + bt_bdaddr_t *bd_addr, uint8_t scope, uint8_t *uid, uint16_t uid_counter) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; +#if (AVRC_CTRL_INCLUDED == TRUE) + rc_transaction_t *p_transaction = NULL; + BTIF_TRACE_DEBUG("%s: scope %d uid_counter %d", __FUNCTION__, scope, uid_counter); + btif_rc_device_cb_t *p_dev = btif_rc_get_device_by_bda(bd_addr); + CHECK_RC_CONNECTED(p_dev); + CHECK_BR_CONNECTED(p_dev); + + bt_status_t tran_status = get_transaction(&p_transaction); + if (BT_STATUS_SUCCESS != tran_status) + return BT_STATUS_FAIL; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + avrc_cmd.pdu = AVRC_PDU_PLAY_ITEM; + avrc_cmd.play_item.opcode = AVRC_OP_VENDOR; + avrc_cmd.play_item.status = AVRC_STS_NO_ERROR; + avrc_cmd.play_item.scope = scope; + memcpy(avrc_cmd.play_item.uid, uid, AVRC_UID_SIZE); + avrc_cmd.play_item.uid_counter = uid_counter; + + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if ((status == AVRC_STS_NO_ERROR) && (p_msg != NULL)) + { + uint8_t* data_start = (uint8_t *)(p_msg + 1) + p_msg->offset; + BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", + __FUNCTION__,p_transaction->lbl); + BTA_AvVendorCmd(p_dev->rc_handle,p_transaction->lbl,AVRC_CMD_CTRL, + data_start, p_msg->len); + status = BT_STATUS_SUCCESS; + // start_control_command_timer (AVRC_PDU_PLAY_ITEM, p_transaction); + } + else + { + BTIF_TRACE_ERROR("%s: failed to build command. status: 0x%02x", + __FUNCTION__, status); + } + osi_free(p_msg); +#else + BTIF_TRACE_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + return (bt_status_t) status; +} + +/*************************************************************************** +** ** Function get_player_app_setting_attr_text_cmd ** ** Description Get text description for app attribute @@ -5309,6 +6061,12 @@ static const btrc_ctrl_interface_t bt_rc_ctrl_interface = { send_passthrough_cmd, send_groupnavigation_cmd, change_player_app_setting, + play_item_cmd, + get_now_playing_list_cmd, + get_folder_list_cmd, + get_player_list_cmd, + change_folder_path_cmd, + set_browsed_player_cmd, set_volume_rsp, volume_change_notification_rsp, cleanup_ctrl, @@ -5466,6 +6224,7 @@ bt_status_t get_transaction(rc_transaction_t **ptransaction) *******************************************************************************/ void release_transaction(uint8_t lbl) { + BTIF_TRACE_DEBUG("%s %d", __func__, lbl); rc_transaction_t *transaction = get_transaction_by_lbl(lbl); /* If the transaction is in use... */ diff --git a/include/bt_target.h b/include/bt_target.h index b48692a44..2adc6fb67 100644 --- a/include/bt_target.h +++ b/include/bt_target.h @@ -275,6 +275,22 @@ #define AVCT_CMD_BUF_SIZE 288 #endif +#ifndef AVCT_USER_TX_BUF_SIZE +#define AVCT_USER_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef AVCT_USER_RX_BUF_SIZE +#define AVCT_USER_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef AVCT_FCR_TX_BUF_SIZE +#define AVCT_FCR_TX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + +#ifndef AVCT_FCR_RX_BUF_SIZE +#define AVCT_FCR_RX_BUF_SIZE BT_DEFAULT_BUFFER_SIZE +#endif + /* AVRCP buffer size for protocol messages */ #ifndef AVRC_CMD_BUF_SIZE #define AVRC_CMD_BUF_SIZE 288 @@ -1372,6 +1388,48 @@ #define AVCT_NUM_CONN 3 #endif +/* AVCTP Browsing channel FCR Option: + * Size of the transmission window when using enhanced retransmission mode. Not used + * in basic and streaming modes. Range: 1 - 63 + */ +#ifndef AVCT_BR_FCR_OPT_TX_WINDOW_SIZE +#define AVCT_BR_FCR_OPT_TX_WINDOW_SIZE 10 +#endif + +/* AVCTP Browsing channel FCR Option: + * Number of transmission attempts for a single I-Frame before taking + * Down the connection. Used In ERTM mode only. Value is Ignored in basic and + * Streaming modes. + * Range: 0, 1-0xFF + * 0 - infinite retransmissions + * 1 - single transmission + */ +#ifndef AVCT_BR_FCR_OPT_MAX_TX_B4_DISCNT +#define AVCT_BR_FCR_OPT_MAX_TX_B4_DISCNT 20 +#endif + +/* AVCTP Browsing channel FCR Option: Retransmission Timeout + * The AVRCP specification set a value in the range of 300 - 2000 ms + * Timeout (in msecs) to detect Lost I-Frames. Only used in Enhanced retransmission mode. + * Range: Minimum 2000 (2 secs) when supporting PBF. + */ +#ifndef AVCT_BR_FCR_OPT_RETX_TOUT +#define AVCT_BR_FCR_OPT_RETX_TOUT 2000 +#endif + +/* AVCTP Browsing channel FCR Option: Monitor Timeout + * The AVRCP specification set a value in the range of 300 - 2000 ms + * Timeout (in msecs) to detect Lost S-Frames. Only used in Enhanced retransmission mode. + * Range: Minimum 12000 (12 secs) when supporting PBF. + */ +#ifndef AVCT_BR_FCR_OPT_MONITOR_TOUT +#define AVCT_BR_FCR_OPT_MONITOR_TOUT 12000 +#endif + +#ifndef AVCT_BROWSE_INCLUDED +#define AVCT_BROWSE_INCLUDED TRUE +#endif + /****************************************************************************** ** ** AVRCP @@ -1394,6 +1452,11 @@ #define DUMP_PCM_DATA FALSE #endif +/* TRUE to support the AVRCP 1.6 (GetTotalNumOfItems). */ +#ifndef AVRC_1_6_INCLUDED +#define AVRC_1_6_INCLUDED TRUE +#endif + /****************************************************************************** ** ** MCAP diff --git a/stack/avct/avct_api.c b/stack/avct/avct_api.c index 7519cb789..1ea88efb9 100644 --- a/stack/avct/avct_api.c +++ b/stack/avct/avct_api.c @@ -402,14 +402,14 @@ uint16_t AVCT_MsgReq(uint8_t handle, uint8_t label, uint8_t cr, BT_HDR *p_msg) tAVCT_CCB *p_ccb; tAVCT_UL_MSG ul_msg; - AVCT_TRACE_API("AVCT_MsgReq"); + AVCT_TRACE_API("%s", __func__); /* verify p_msg parameter */ if (p_msg == NULL) { return AVCT_NO_RESOURCES; } - AVCT_TRACE_API("len: %d", p_msg->len); + AVCT_TRACE_API("%s len: %d layer_specific: %d", __func__, p_msg->len, p_msg->layer_specific); /* map handle to ccb */ if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) diff --git a/stack/avct/avct_bcb_act.c b/stack/avct/avct_bcb_act.c index 520c81131..ac5cb3391 100644 --- a/stack/avct/avct_bcb_act.c +++ b/stack/avct/avct_bcb_act.c @@ -435,7 +435,6 @@ void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) *******************************************************************************/ void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) { - osi_free_and_reset((void **)&p_bcb->p_tx_msg); /* if control channel is up, save the message and open the browsing channel */ diff --git a/stack/avct/avct_l2c_br.c b/stack/avct/avct_l2c_br.c index cf026c2c5..6dee7995a 100644 --- a/stack/avct/avct_l2c_br.c +++ b/stack/avct/avct_l2c_br.c @@ -66,7 +66,6 @@ */ #define AVCT_BR_FCR_OPT_MONITOR_TOUT 12000 - /* callback function declarations */ void avct_l2c_br_connect_ind_cback(BD_ADDR bd_addr, uint16_t lcid, uint16_t psm, uint8_t id); void avct_l2c_br_connect_cfm_cback(uint16_t lcid, uint16_t result); @@ -77,7 +76,6 @@ void avct_l2c_br_disconnect_cfm_cback(uint16_t lcid, uint16_t result); void avct_l2c_br_congestion_ind_cback(uint16_t lcid, bool is_congested); void avct_l2c_br_data_ind_cback(uint16_t lcid, BT_HDR *p_buf); - /* L2CAP callback function structure */ const tL2CAP_APPL_INFO avct_l2c_br_appl = { avct_l2c_br_connect_ind_cback, diff --git a/stack/avrc/avrc_bld_ct.c b/stack/avrc/avrc_bld_ct.c index ab3391320..bb7afb1ff 100644 --- a/stack/avrc/avrc_bld_ct.c +++ b/stack/avrc/avrc_bld_ct.c @@ -331,6 +331,34 @@ static tAVRC_STS avrc_bld_get_element_attr_cmd(BT_HDR * p_pkt, uint8_t num_attri /******************************************************************************* ** +** Function avrc_bld_play_item_cmd +** +** Description This function builds the play item cmd +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_play_item_cmd( + BT_HDR * p_pkt, uint8_t scope, uint8_t *uid, uint16_t uid_counter) +{ + AVRC_TRACE_API("avrc_bld_get_element_attr_cmd"); + uint8_t *p_start = (uint8_t *)(p_pkt + 1) + p_pkt->offset; + uint8_t *p_data = p_start + 2; /* pdu + rsvd */ + /* add fixed length 11 */ + UINT16_TO_BE_STREAM(p_data, 0xb); + /* Add scope */ + UINT8_TO_BE_STREAM(p_data, scope); + /* Add UID */ + ARRAY_TO_BE_STREAM(p_data, uid, AVRC_UID_SIZE); + /* Add UID Counter */ + UINT16_TO_BE_STREAM(p_data, uid_counter); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** ** Function avrc_bld_get_play_status_cmd ** ** Description This function builds the get play status command. @@ -344,11 +372,97 @@ static tAVRC_STS avrc_bld_get_play_status_cmd(BT_HDR * p_pkt) AVRC_TRACE_API("avrc_bld_list_player_app_attr_cmd"); uint8_t *p_start = (uint8_t *)(p_pkt + 1) + p_pkt->offset; uint8_t *p_data = p_start + 2; /* pdu + rsvd */ - /* add fixed length 1 -*/ + /* add fixed length 0 -*/ UINT16_TO_BE_STREAM(p_data, 0); p_pkt->len = (p_data - p_start); return AVRC_STS_NO_ERROR; } + +/******************************************************************************* +** +** Function avrc_bld_get_folder_items_cmd +** +** Description This function builds the get folder items cmd. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_folder_items_cmd(BT_HDR *p_pkt, const tAVRC_GET_ITEMS_CMD *cmd) +{ + AVRC_TRACE_API("avrc_bld_get_folder_items_cmd"); + uint8_t *p_start = (uint8_t *)(p_pkt + 1) + p_pkt->offset; + /* This is where the PDU specific for AVRC starts + * AVRCP Spec 1.4 section 22.19 */ + uint8_t *p_data = p_start + 1; /* pdu */ + + /* To get the list of all media players we simply need to use the predefined + * PDU mentioned in above spec. */ + /* scope (1) + st item (4) + end item (4) + attr (1) */ + UINT16_TO_BE_STREAM(p_data, 10); + UINT8_TO_BE_STREAM(p_data, cmd->scope); /* scope (1bytes) */ + UINT32_TO_BE_STREAM(p_data, cmd->start_item); /* start item (4bytes) */ + UINT32_TO_BE_STREAM(p_data, cmd->end_item); /* end item (4bytes) */ + UINT8_TO_BE_STREAM(p_data, 0); /* attribute count = 0 (1bytes) */ + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_change_folder_cmd +** +** Description This function builds the change folder command +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_change_folder_cmd(BT_HDR *p_pkt, const tAVRC_CHG_PATH_CMD *cmd) +{ + AVRC_TRACE_API("avrc_bld_change_folder_cmd"); + uint8_t *p_start = (uint8_t *)(p_pkt + 1) + p_pkt->offset; + /* This is where the PDU specific for AVRC starts + * AVRCP Spec 1.4 section 22.19 */ + uint8_t *p_data = p_start + 1; /* pdu */ + + /* To change folder we need to provide the following: + * UID Counter (2) + Direction (1) + UID (8) = 11bytes + */ + UINT16_TO_BE_STREAM(p_data, 11); + UINT16_TO_BE_STREAM(p_data, cmd->uid_counter); + UINT8_TO_BE_STREAM(p_data, cmd->direction); + ARRAY_TO_BE_STREAM(p_data, cmd->folder_uid, AVRC_UID_SIZE); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_set_browsed_player_cmd +** +** Description This function builds the set addressed player cmd. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_set_browsed_player_cmd(BT_HDR *p_pkt, const tAVRC_SET_BR_PLAYER_CMD *cmd) +{ + AVRC_TRACE_API("%s", __func__); + uint8_t *p_start = (uint8_t *)(p_pkt + 1) + p_pkt->offset; + /* This is where the PDU specific for AVRC starts + * AVRCP Spec 1.4 section 22.19 */ + uint8_t *p_data = p_start + 1; /* pdu */ + + /* To change browsed player the following is the total length: + * Player ID (2) + */ + UINT16_TO_BE_STREAM(p_data, 2); /* fixed length */ + UINT16_TO_BE_STREAM(p_data, cmd->player_id); + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} #endif /******************************************************************************* @@ -363,12 +477,18 @@ static tAVRC_STS avrc_bld_get_play_status_cmd(BT_HDR * p_pkt) *******************************************************************************/ static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) { - uint8_t opcode = avrc_opcode_from_pdu(p_cmd->pdu); + uint16_t chnl = AVCT_DATA_CTRL; + uint8_t opcode = avrc_opcode_from_pdu(p_cmd->pdu); AVRC_TRACE_API("avrc_bld_init_cmd_buffer: pdu=%x, opcode=%x", p_cmd->pdu, opcode); uint16_t offset = 0; switch (opcode) { + case AVRC_OP_BROWSE: + chnl = AVCT_DATA_BROWSE; + offset = AVCT_BROWSE_OFFSET; + break; + case AVRC_OP_PASS_THRU: offset = AVRC_MSG_PASS_THRU_OFFSET; break; @@ -382,7 +502,7 @@ static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) BT_HDR *p_pkt = (BT_HDR *)osi_malloc(AVRC_META_CMD_BUF_SIZE); uint8_t *p_data, *p_start; - p_pkt->layer_specific = AVCT_DATA_CTRL; + p_pkt->layer_specific = chnl; p_pkt->event = opcode; p_pkt->offset = offset; p_data = (uint8_t *)(p_pkt + 1) + p_pkt->offset; @@ -490,9 +610,22 @@ tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt) status = avrc_bld_get_element_attr_cmd(p_pkt, p_cmd->get_elem_attrs.num_attr,p_cmd->get_elem_attrs.attrs); break; + case AVRC_PDU_PLAY_ITEM: + status = avrc_bld_play_item_cmd( + p_pkt, p_cmd->play_item.scope, p_cmd->play_item.uid, p_cmd->play_item.uid_counter); + break; case AVRC_PDU_GET_PLAY_STATUS: status = avrc_bld_get_play_status_cmd(p_pkt); break; + case AVRC_PDU_GET_FOLDER_ITEMS: + status = avrc_bld_get_folder_items_cmd(p_pkt, &(p_cmd->get_items)); + break; + case AVRC_PDU_CHANGE_PATH: + status = avrc_bld_change_folder_cmd(p_pkt, &(p_cmd->chg_path)); + break; + case AVRC_PDU_SET_BROWSED_PLAYER: + status = avrc_bld_set_browsed_player_cmd(p_pkt, &(p_cmd->br_player)); + break; #endif } diff --git a/stack/avrc/avrc_int.h b/stack/avrc/avrc_int.h index 53b79f8f0..d55c061ad 100644 --- a/stack/avrc/avrc_int.h +++ b/stack/avrc/avrc_int.h @@ -29,7 +29,6 @@ #include "avct_defs.h" #include "avrc_api.h" #include "osi/include/alarm.h" - #include "osi/include/fixed_queue.h" #ifdef __cplusplus @@ -132,8 +131,8 @@ typedef struct /* AVRC internal connection control block */ typedef struct { - fixed_queue_t *cmd_q; /* Command queue for serializing vendor specific commands */ - uint8_t flags; /* See AVRC_CB_FLAGS_* definitions */ + fixed_queue_t *cmd_q; /* Command queue for serializing vendor specific commands */ + uint8_t flags; /* See AVRC_CB_FLAGS_* definitions */ alarm_t * tle; /* Command timeout timer */ } tAVRC_CONN_INT_CB; diff --git a/stack/avrc/avrc_pars_ct.c b/stack/avrc/avrc_pars_ct.c index fd58c7763..aa3e1c795 100644 --- a/stack/avrc/avrc_pars_ct.c +++ b/stack/avrc/avrc_pars_ct.c @@ -150,6 +150,248 @@ void avrc_parse_notification_rsp (uint8_t *p_stream, tAVRC_REG_NOTIF_RSP *p_rsp) } } +static tAVRC_STS avrc_pars_browse_rsp(tAVRC_MSG_BROWSE *p_msg, tAVRC_RESPONSE *p_rsp) +{ + tAVRC_STS status = AVRC_STS_NO_ERROR; + uint8_t pdu; + + if (p_msg->browse_len == 0) + { + AVRC_TRACE_ERROR("%s length ", p_msg->browse_len); + return AVRC_STS_BAD_PARAM; + } + + uint8_t *p = p_msg->p_browse_data; + + /* read the pdu */ + BE_STREAM_TO_UINT8(pdu, p); + uint8_t pkt_len; + /* read the entire packet len */ + BE_STREAM_TO_UINT16(pkt_len, p); + + AVRC_TRACE_DEBUG("%s pdu %d", __func__, pdu); + + /* used to track how much we have read, if we cannot read anymore but the + * packet says so then we have a malformed packet. Also vice versa. */ + uint16_t pkt_len_read = 0; + + + switch (pdu) + { + case AVRC_PDU_GET_FOLDER_ITEMS: + { + tAVRC_GET_ITEMS_RSP *get_item_rsp = &(p_rsp->get_items); + /* Copy back the PDU */ + get_item_rsp->pdu = pdu; + /* read the status */ + BE_STREAM_TO_UINT8(get_item_rsp->status, p); + /* read the UID counter */ + BE_STREAM_TO_UINT16(get_item_rsp->uid_counter, p); + /* read the number of items */ + BE_STREAM_TO_UINT16(get_item_rsp->item_count, p); + pkt_len_read += 5; + + AVRC_TRACE_DEBUG("%s pdu %d status %d pkt_len %d uid counter %d item count %d", + __func__, get_item_rsp->pdu, get_item_rsp->status, pkt_len, + get_item_rsp->uid_counter, get_item_rsp->item_count); + + /* get each of the items */ + get_item_rsp->p_item_list = + (tAVRC_ITEM *) osi_malloc (get_item_rsp->item_count * (sizeof(tAVRC_ITEM))); + tAVRC_ITEM *curr_item = get_item_rsp->p_item_list; + for (int i = 0; i < get_item_rsp->item_count; i++) + { + BE_STREAM_TO_UINT8(curr_item->item_type, p); + pkt_len_read += 1; + AVRC_TRACE_DEBUG("%s item type %d", __func__, curr_item->item_type); + switch (curr_item->item_type) + { + case AVRC_ITEM_PLAYER: + { + /* Handle player */ + tAVRC_ITEM_PLAYER *player = &(curr_item->u.player); + uint8_t player_len; + BE_STREAM_TO_UINT16(player_len, p); + BE_STREAM_TO_UINT16(player->player_id, p); + BE_STREAM_TO_UINT8(player->major_type, p); + BE_STREAM_TO_UINT32(player->sub_type, p); + BE_STREAM_TO_UINT8(player->play_status, p); + BE_STREAM_TO_ARRAY(p, player->features, AVRC_FEATURE_MASK_SIZE); + pkt_len_read += (10 + AVRC_FEATURE_MASK_SIZE); + + /* read str */ + BE_STREAM_TO_UINT16(player->name.charset_id, p); + BE_STREAM_TO_UINT16(player->name.str_len, p); + player->name.p_str = (uint8_t *) osi_malloc ( + (player->name.str_len + 1) * sizeof (uint8_t)); + BE_STREAM_TO_ARRAY(p, player->name.p_str, player->name.str_len); + pkt_len_read += (4 + player->name.str_len); + AVRC_TRACE_DEBUG( + "%s type %d id %d mtype %d stype %d ps %d cs %d name len %d", + __func__, curr_item->item_type, player->player_id, + player->major_type, player->sub_type, player->play_status, + player->name.charset_id, player->name.str_len); + } break; + + case AVRC_ITEM_FOLDER: + { + tAVRC_ITEM_FOLDER *folder = &(curr_item->u.folder); + uint16_t folder_len; + BE_STREAM_TO_UINT16(folder_len, p); + + BE_STREAM_TO_ARRAY(p, folder->uid, AVRC_UID_SIZE); + BE_STREAM_TO_UINT8(folder->type, p); + BE_STREAM_TO_UINT8(folder->playable, p); + pkt_len_read += (4 + AVRC_UID_SIZE); + + /* read str, encoding to be handled by upper layers */ + BE_STREAM_TO_UINT16(folder->name.charset_id, p); + BE_STREAM_TO_UINT16(folder->name.str_len, p); + folder->name.p_str = (uint8_t *) osi_malloc ( + (folder->name.str_len + 1) * sizeof (uint8_t)); + BE_STREAM_TO_ARRAY(p, folder->name.p_str, folder->name.str_len); + pkt_len_read += (4 + folder->name.str_len); + AVRC_TRACE_DEBUG( + "%s type %d playable %d cs %d name len %d", + __func__, folder->type, folder->playable, folder->name.charset_id, + folder->name.str_len); + } break; + + case AVRC_ITEM_MEDIA: + { + tAVRC_ITEM_MEDIA *media = &(curr_item->u.media); + uint8_t media_len; + BE_STREAM_TO_UINT16(media_len, p); + BE_STREAM_TO_ARRAY(p, media->uid, AVRC_UID_SIZE); + BE_STREAM_TO_UINT8(media->type, p); + pkt_len_read += (3 + AVRC_UID_SIZE); + + /* read str, encoding to be handled by upper layers */ + BE_STREAM_TO_UINT16(media->name.charset_id, p); + BE_STREAM_TO_UINT16(media->name.str_len, p); + media->name.p_str = (uint8_t *) osi_malloc ( + (media->name.str_len) * sizeof (uint8_t)); + BE_STREAM_TO_ARRAY(p, media->name.p_str, media->name.str_len); + + BE_STREAM_TO_UINT8(media->attr_count, p); + AVRC_TRACE_DEBUG("%s media type %d charset id %d len %d attr ct %d", + __func__, media->type, media->name.charset_id, + media->name.str_len, media->attr_count); + pkt_len_read += (5 + media->name.str_len); + + media->p_attr_list = (tAVRC_ATTR_ENTRY *) osi_malloc ( + media->attr_count * sizeof (tAVRC_ATTR_ENTRY)); + for (int jk = 0; jk < media->attr_count; jk++) + { + tAVRC_ATTR_ENTRY *attr_entry = &(media->p_attr_list[jk]); + BE_STREAM_TO_UINT32(attr_entry->attr_id, p); + + /* Parse the name now */ + BE_STREAM_TO_UINT16(attr_entry->name.charset_id, p); + BE_STREAM_TO_UINT16(attr_entry->name.str_len, p); + attr_entry->name.p_str = (uint8_t *) osi_malloc ( + attr_entry->name.str_len * sizeof (uint8_t)); + BE_STREAM_TO_ARRAY(p, attr_entry->name.p_str, attr_entry->name.str_len); + pkt_len_read += (8 + attr_entry->name.str_len); + AVRC_TRACE_DEBUG("%s media attr id %d cs %d name len %d", + __func__, attr_entry->attr_id, + attr_entry->name.charset_id, attr_entry->name.str_len); + } + } break; + + default: + AVRC_TRACE_ERROR("%s item type not handled %d", __func__, curr_item->item_type); + return AVRC_STS_INTERNAL_ERR; + } + + /* we check if we have overrun */ + if (pkt_len_read > pkt_len) + { + AVRC_TRACE_ERROR("%s overflow in read pkt_len %d pkt_len_read %d", + __func__, pkt_len, pkt_len_read); + return AVRC_STS_BAD_CMD; + } + AVRC_TRACE_DEBUG("%s pkt_len %d pkt_len_read %d", __func__, pkt_len, pkt_len_read); + + /* advance to populate the next item */ + curr_item++; + } + break; + } + + case AVRC_PDU_CHANGE_PATH: + { + tAVRC_CHG_PATH_RSP *change_path_rsp = &(p_rsp->chg_path); + /* Copyback the PDU */ + change_path_rsp->pdu = pdu; + /* Read the status */ + BE_STREAM_TO_UINT8(change_path_rsp->status, p); + /* Read the number of items in folder */ + BE_STREAM_TO_UINT32(change_path_rsp->num_items, p); + pkt_len_read += 5; + + AVRC_TRACE_DEBUG("%s pdu %d status %d item count %d", + __func__, change_path_rsp->pdu, change_path_rsp->status, + change_path_rsp->num_items); + break; + } + + case AVRC_PDU_SET_BROWSED_PLAYER: + { + tAVRC_SET_BR_PLAYER_RSP *set_br_pl_rsp = &(p_rsp->br_player); + /* Copyback the PDU */ + set_br_pl_rsp->pdu = pdu; + + /* Read the status */ + BE_STREAM_TO_UINT8(set_br_pl_rsp->status, p); + + if (set_br_pl_rsp->status != AVRC_STS_NO_ERROR) { + AVRC_TRACE_ERROR( + "%s Stopping further parsing because player not browsable sts %d", + __func__, set_br_pl_rsp->status); + break; + } + BE_STREAM_TO_UINT16(set_br_pl_rsp->uid_counter, p); + BE_STREAM_TO_UINT32(set_br_pl_rsp->num_items, p); + BE_STREAM_TO_UINT16(set_br_pl_rsp->charset_id, p); + BE_STREAM_TO_UINT8(set_br_pl_rsp->folder_depth, p); + AVRC_TRACE_DEBUG("%s AVRC_PDU_SET_BROWSED_PLAYER status %d items %d cs %d depth %d", + __func__, + set_br_pl_rsp->status, + set_br_pl_rsp->num_items, + set_br_pl_rsp->charset_id, + set_br_pl_rsp->folder_depth); + pkt_len_read += 10; + + set_br_pl_rsp->p_folders = (tAVRC_NAME *) osi_malloc ( + set_br_pl_rsp->num_items * sizeof (tAVRC_NAME)); + + /* Read each of the folder in the depth */ + for (uint32_t i = 0; i < set_br_pl_rsp->folder_depth; i++) { + tAVRC_NAME *folder_name = &(set_br_pl_rsp->p_folders[i]); + BE_STREAM_TO_UINT16(folder_name->str_len, p); + AVRC_TRACE_DEBUG("%s AVRC_PDU_SET_BROWSED_PLAYER item: %d len: %d", + __func__, i, folder_name->str_len); + folder_name->p_str = (uint8_t *) osi_malloc ( + (folder_name->str_len + 1) * sizeof (uint8_t)); + BE_STREAM_TO_ARRAY(p, folder_name->p_str, folder_name->str_len); + pkt_len_read += (2 + folder_name->str_len); + } + break; + } + + default: + AVRC_TRACE_ERROR("%s pdu %d not handled", __func__, pdu); + } + + if (pkt_len != pkt_len_read) + { + AVRC_TRACE_ERROR("%s finished pkt_len %d pkt_len_read %d", __func__, pkt_len, pkt_len_read); + return AVRC_STS_BAD_CMD; + } + return status; +} + #if (AVRC_CTRL_INCLUDED == TRUE) /******************************************************************************* ** @@ -398,6 +640,10 @@ tAVRC_STS AVRC_Ctrl_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result, ui status = avrc_ctrl_pars_vendor_rsp(&p_msg->vendor, p_result, p_buf,buf_len); break; + case AVRC_OP_BROWSE: /* 0xff Browse commands */ + status = avrc_pars_browse_rsp(&p_msg->browse, p_result); + break; + default: AVRC_TRACE_ERROR("%s unknown opcode:0x%x", __func__, p_msg->hdr.opcode); break; diff --git a/stack/include/avrc_defs.h b/stack/include/avrc_defs.h index 018eaacd2..f86505440 100644 --- a/stack/include/avrc_defs.h +++ b/stack/include/avrc_defs.h @@ -853,7 +853,7 @@ typedef union ((a) <= AVRC_EVT_VOLUME_CHANGE)) ? true : false) #define AVRC_IS_VALID_ATTRIBUTE(a) ((((((a) > 0) && (a) <= AVRC_PLAYER_SETTING_SCAN)) || \ - ((a) >= AVRC_PLAYER_SETTING_LOW_MENU_EXT)) ? true : false) + ((a) >= AVRC_PLAYER_SETTING_LOW_MENU_EXT)) ? true : false) #define AVRC_IS_VALID_MEDIA_ATTRIBUTE(a) (((a) >= AVRC_MEDIA_ATTR_ID_TITLE) && \ ((a) <= AVRC_MEDIA_ATTR_ID_PLAYING_TIME) ? true : false) @@ -1166,7 +1166,7 @@ typedef struct typedef struct { uint8_t pdu; - tAVRC_STS status; + tAVRC_STS status; uint8_t opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ uint8_t scope; } tAVRC_GET_NUM_OF_ITEMS_CMD; -- 2.11.0