diff options
author | Kausik Sinnaswamy <kausik@broadcom.com> | 2012-03-09 10:54:02 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:13 -0700 |
commit | 837acf40a4968449f044cfd3cd15f7f120617eb5 (patch) | |
tree | d2e71deec39c3a5e2e9651726662748fdf6440ac /btif/src | |
parent | 5140bed9a19aadca962184ebc9ed823d5d7cb745 (diff) | |
download | external_bluetooth_bluedroid-837acf40a4968449f044cfd3cd15f7f120617eb5.zip external_bluetooth_bluedroid-837acf40a4968449f044cfd3cd15f7f120617eb5.tar.gz external_bluetooth_bluedroid-837acf40a4968449f044cfd3cd15f7f120617eb5.tar.bz2 |
Added support to handle AVRCP 1.0 and incoming A2DP connections
Change-Id: I7863920895aa9fd77eccee4083319701bcbf4300
Diffstat (limited to 'btif/src')
-rw-r--r-- | btif/src/btif_av.c | 137 | ||||
-rw-r--r-- | btif/src/btif_rc.c | 435 |
2 files changed, 560 insertions, 12 deletions
diff --git a/btif/src/btif_av.c b/btif/src/btif_av.c index d383926..a822c7f 100644 --- a/btif/src/btif_av.c +++ b/btif/src/btif_av.c @@ -62,12 +62,16 @@ #include "bta_api.h" #include "bta_av_api.h" #include "gki.h" +#include "bd.h" +#include "btu.h" /***************************************************************************** ** Constants & Macros ******************************************************************************/ #define BTIF_AV_SERVICE_NAME "Advanced Audio" +#define BTIF_TIMEOUT_AV_OPEN_ON_RC 2 /* 2 seconds */ + typedef enum { BTIF_AV_STATE_IDLE = 0x0, BTIF_AV_STATE_OPENING, @@ -99,6 +103,8 @@ typedef struct static btav_callbacks_t *bt_av_callbacks = NULL; static btif_av_cb_t btif_av_cb; +static TIMER_LIST_ENT tle_av_open_on_rc; + #define CHECK_BTAV_INIT() if (bt_av_callbacks == NULL)\ {\ BTIF_TRACE_WARNING1("%s: BTAV not initialized", __FUNCTION__);\ @@ -109,6 +115,18 @@ else\ BTIF_TRACE_EVENT1("%s", __FUNCTION__);\ } +/* 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_REMOTE_CMD_EVT: \ + case BTA_AV_VENDOR_CMD_EVT: \ + case BTA_AV_META_MSG_EVT: \ + case BTA_AV_RC_FEAT_EVT: \ + { \ + btif_rc_handler(e, d);\ + }break; \ + static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *data); static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *data); static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *data); @@ -123,6 +141,13 @@ static const btif_sm_handler_t btif_av_state_handlers[] = }; +/************************************************************************* +** Extern functions +*************************************************************************/ +extern void btif_rc_init(void); +extern void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data); +extern BOOLEAN btif_rc_get_connected_peer(BD_ADDR peer_addr); + /***************************************************************************** ** Local helper functions ******************************************************************************/ @@ -173,6 +198,36 @@ const char *dump_av_sm_event_name(btif_av_sm_event_t event) } } + +/**************************************************************************** +** Local helper functions +*****************************************************************************/ +/******************************************************************************* +** +** Function btif_initiate_av_open_tmr_hdlr +** +** Description Timer to trigger AV open if the remote headset establishes +** RC connection w/o AV connection. The timer is needed to IOP +** with headsets that do establish AV after RC connection. +** +** Returns void +** +*******************************************************************************/ +static void btif_initiate_av_open_tmr_hdlr(TIMER_LIST_ENT *tle) +{ + BD_ADDR peer_addr; + + /* is there at least one RC connection - There should be */ + if (btif_rc_get_connected_peer(peer_addr)) { + BTIF_TRACE_DEBUG1("%s Issuing connect to the remote RC peer", __FUNCTION__); + btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (void*)&peer_addr); + } + else + { + BTIF_TRACE_ERROR1("%s No connected RC peers", __FUNCTION__); + } +} + /***************************************************************************** ** Static functions ******************************************************************************/ @@ -210,16 +265,54 @@ static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data) { btif_av_cb.bta_handle = ((tBTA_AV*)p_data)->registr.hndl; } break; - + case BTA_AV_PENDING_EVT: case BTIF_AV_CONNECT_REQ_EVT: { + if (event == BTIF_AV_CONNECT_REQ_EVT) + { memcpy(&btif_av_cb.peer_bda, (bt_bdaddr_t*)p_data, sizeof(bt_bdaddr_t)); - BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle, TRUE, BTA_SEC_NONE); - + } + else if (event == BTA_AV_PENDING_EVT) + { + bdcpy(btif_av_cb.peer_bda.address, ((tBTA_AV*)p_data)->pend.bd_addr); + } btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING); } break; + case BTA_AV_RC_OPEN_EVT: + { + /* IOP_FIX: Jabra 620 only does RC open without AV open whenever it connects. So + * as per the AV WP, an AVRC connection cannot exist without an AV connection. Therefore, + * we initiate an AV connection if an RC_OPEN_EVT is received when we are in AV_CLOSED state. + * We initiate the AV connection after a small 3s timeout to avoid any collisions from the + * headsets, as some headsets initiate the AVRC connection first and then + * immediately initiate the AV connection + * + * TODO: We may need to do this only on an AVRCP Play. FixMe + */ + BTIF_TRACE_DEBUG0("BTA_AV_RC_OPEN_EVT received w/o AV"); + memset(&tle_av_open_on_rc, 0, sizeof(tle_av_open_on_rc)); + tle_av_open_on_rc.param = (UINT32)btif_initiate_av_open_tmr_hdlr; + btu_start_timer(&tle_av_open_on_rc, BTU_TTYPE_USER_FUNC, + BTIF_TIMEOUT_AV_OPEN_ON_RC); + btif_rc_handler(event, p_data); + }break; + case BTA_AV_REMOTE_CMD_EVT: + case BTA_AV_VENDOR_CMD_EVT: + case BTA_AV_META_MSG_EVT: + case BTA_AV_RC_FEAT_EVT: + { + btif_rc_handler(event, (tBTA_AV*)p_data); + }break; + case BTA_AV_RC_CLOSE_EVT: + { + if (tle_av_open_on_rc.in_use) { + BTIF_TRACE_DEBUG0("BTA_AV_RC_CLOSE_EVT: Stopping AV timer."); + btu_stop_timer(&tle_av_open_on_rc); + } + btif_rc_handler(event, p_data); + }break; default: BTIF_TRACE_WARNING2("%s Unhandled event:%s", __FUNCTION__, @@ -228,7 +321,6 @@ static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data) return TRUE; } - static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data) { BTIF_TRACE_DEBUG2("%s event:%s", __FUNCTION__, dump_av_sm_event_name(event)); @@ -248,26 +340,30 @@ static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data case BTA_AV_OPEN_EVT: { tBTA_AV *p_bta_data = (tBTA_AV*)p_data; - btav_connection_state_t state = BTAV_CONNECTION_STATE_DISCONNECTED; + btav_connection_state_t state; + btif_sm_state_t av_state; BTIF_TRACE_DEBUG1("status:%d", p_bta_data->open.status); if (p_bta_data->open.status == BTA_AV_SUCCESS) { state = BTAV_CONNECTION_STATE_CONNECTED; - /* change state to open */ - btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED); + av_state = BTIF_AV_STATE_OPENED; } else { BTIF_TRACE_WARNING1("BTA_AV_OPEN_EVT::FAILED status: %d", p_bta_data->open.status ); - /* change state to idle */ - btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE); + state = BTAV_CONNECTION_STATE_DISCONNECTED; + av_state = BTIF_AV_STATE_IDLE; } /* inform the application of the event */ CHECK_CALL_CBACK(bt_av_callbacks, connection_state_cb, state, &(btif_av_cb.peer_bda)); + /* change state to open/idle based on the status */ + btif_sm_change_state(btif_av_cb.sm_handle, av_state); } break; + CHECK_RC_EVENT(event, p_data); + default: BTIF_TRACE_WARNING2("%s Unhandled event:%s", __FUNCTION__, dump_av_sm_event_name(event)); @@ -284,7 +380,15 @@ static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data) { case BTIF_SM_ENTER_EVT: - BTIF_TRACE_DEBUG0("starting av"); + BTIF_TRACE_DEBUG0("starting av...sleeping before that"); + + /* Auto-starting on Open could introduce race conditions. So delaying + * the start. + * + * This is anyway temporary and will be removed once the START/STOP stream + * requests are processed. + */ + GKI_delay(3000); btif_a2dp_upon_start_req(); @@ -331,6 +435,8 @@ static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data) btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE); } break; + CHECK_RC_EVENT(event, p_data); + default: BTIF_TRACE_WARNING2("%s Unhandled event:%s", __FUNCTION__, dump_av_sm_event_name(event)); @@ -338,7 +444,7 @@ static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data) return TRUE; } -static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *data) +static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *p_data) { BTIF_TRACE_DEBUG2("%s event:%s", __FUNCTION__, dump_av_sm_event_name(event)); @@ -364,6 +470,8 @@ static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *data) btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED); } break; + CHECK_RC_EVENT(event, p_data); + default: BTIF_TRACE_WARNING2("%s Unhandled event:%s", __FUNCTION__, dump_av_sm_event_name(event)); @@ -414,6 +522,8 @@ static bt_status_t init(btav_callbacks_t* callbacks ) bt_av_callbacks = callbacks; btif_enable_service(BTA_A2DP_SERVICE_ID); + /* Initialize the AVRC CB */ + btif_rc_init(); /* Also initialize the AV state machine */ btif_av_cb.sm_handle = btif_sm_init((const btif_sm_handler_t*)btif_av_state_handlers, BTIF_AV_STATE_IDLE); @@ -516,7 +626,10 @@ bt_status_t btif_av_execute_service(BOOLEAN b_enable) BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR, bte_av_callback); #else - BTA_AvEnable(BTA_SEC_AUTHENTICATE|BTA_SEC_AUTHORIZE, BTA_AV_FEAT_RCTG, + /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not + * handle this request in order to allow incoming connections to succeed. + * We need to put this back once support for this is added */ + BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_RCTG, bte_av_callback); #endif BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0); diff --git a/btif/src/btif_rc.c b/btif/src/btif_rc.c new file mode 100644 index 0000000..04d5681 --- /dev/null +++ b/btif/src/btif_rc.c @@ -0,0 +1,435 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * This program is the proprietary software of Broadcom Corporation and/or its + * licensors, and may only be used, duplicated, modified or distributed + * pursuant to the terms and conditions of a separate, written license + * agreement executed between you and Broadcom (an "Authorized License"). + * Except as set forth in an Authorized License, Broadcom grants no license + * (express or implied), right to use, or waiver of any kind with respect to + * the Software, and Broadcom expressly reserves all rights in and to the + * Software and all intellectual property rights therein. + * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS + * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE + * ALL USE OF THE SOFTWARE. + * + * Except as expressly set forth in the Authorized License, + * + * 1. This program, including its structure, sequence and organization, + * constitutes the valuable trade secrets of Broadcom, and you shall + * use all reasonable efforts to protect the confidentiality thereof, + * and to use this information only in connection with your use of + * Broadcom integrated circuit products. + * + * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED + * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, + * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, + * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY + * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, + * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, + * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING + * OUT OF USE OR PERFORMANCE OF THE SOFTWARE. + * + * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM + * OR ITS LICENSORS BE LIABLE FOR + * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY + * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO + * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR + * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE + * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE + * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF + * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. + * + *****************************************************************************/ + + +/***************************************************************************** + * + * Filename: btif_rc.c + * + * Description: Bluetooth AVRC implementation + * + *****************************************************************************/ +#include <hardware/bluetooth.h> +#include <fcntl.h> +#include "bta_api.h" +#include "bta_av_api.h" +#include "avrc_defs.h" +#include "bd.h" +#include "gki.h" + +#define LOG_TAG "BTIF_RC" +#include "btif_common.h" + +/***************************************************************************** +** Constants & Macros +******************************************************************************/ +#if (!defined(AVRC_METADATA_INCLUDED) || (AVRC_METADATA_INCLUDED == FALSE)) +#define BTIF_RC_USE_UINPUT TRUE +#include "uinput.h" +#else +#error "AVRCP 1.3 is not supported on Bluedroid yet." +#endif + +/***************************************************************************** +** Local type definitions +******************************************************************************/ +typedef struct { + BOOLEAN rc_connected; + UINT8 rc_handle; + BD_ADDR rc_addr; + UINT16 rc_pending_play; +} btif_rc_cb_t; + +#ifdef BTIF_RC_USE_UINPUT +#define MAX_UINPUT_PATHS 3 +static const char* uinput_dev_path[] = + {"/dev/uinput", "/dev/input/uinput", "/dev/misc/uinput" }; +static int uinput_fd = -1; + +static int send_event (int fd, uint16_t type, uint16_t code, int32_t value); +static void send_key (int fd, uint16_t key, int pressed); +static int uinput_driver_check(); +static int uinput_create(char *name); +static int init_uinput (void); +static void close_uinput (void); + +static struct { + const char *name; + uint8_t avrcp; + uint16_t mapped_id; + uint8_t release_quirk; +} key_map[] = { + { "PLAY", AVRC_ID_PLAY, KEY_PLAYCD, 1 }, + { "STOP", AVRC_ID_STOP, KEY_STOPCD, 0 }, + { "PAUSE", AVRC_ID_PAUSE, KEY_PAUSECD, 1 }, + { "FORWARD", AVRC_ID_FORWARD, KEY_NEXTSONG, 0 }, + { "BACKWARD", AVRC_ID_BACKWARD, KEY_PREVIOUSSONG, 0 }, + { "REWIND", AVRC_ID_REWIND, KEY_REWIND, 0 }, + { "FAST FORWARD", AVRC_ID_FAST_FOR, KEY_FORWARD, 0 }, + { NULL, 0, 0, 0 } +}; +#endif /* BTIF_RC_USE_UINPUT */ + + +/***************************************************************************** +** Static variables +******************************************************************************/ +static btif_rc_cb_t btif_rc_cb; + +/***************************************************************************** +** Static functions +******************************************************************************/ + +/***************************************************************************** +** Externs +******************************************************************************/ + +/***************************************************************************** +** Functions +******************************************************************************/ + + +#ifdef BTIF_RC_USE_UINPUT +/***************************************************************************** +** Local uinput helper functions +******************************************************************************/ +int send_event (int fd, uint16_t type, uint16_t code, int32_t value) +{ + struct uinput_event event; + + memset(&event, 0, sizeof(event)); + event.type = type; + event.code = code; + event.value = value; + + return write(fd, &event, sizeof(event)); +} + +void send_key (int fd, uint16_t key, int pressed) +{ + if (fd < 0) { + return; + } + + BTIF_TRACE_DEBUG3("AVRCP: Send key %d (%d) fd=%d", key, pressed, fd); + send_event(fd, EV_KEY, key, pressed); + send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +/************** uinput related functions **************/ +int uinput_driver_check() +{ + uint32_t i; + for (i=0; i < MAX_UINPUT_PATHS; i++) + { + if (access(uinput_dev_path[i], O_RDWR) == 0) { + return 0; + } + } + BTIF_TRACE_ERROR1("%s ERROR: uinput device is not in the system", __FUNCTION__); + return -1; +} + +int uinput_create(char *name) +{ + struct uinput_dev dev; + int fd, err, x = 0; + + for(x=0; x < MAX_UINPUT_PATHS; x++) + { + fd = open(uinput_dev_path[x], O_RDWR); + if (fd < 0) + continue; + break; + } + if (x == MAX_UINPUT_PATHS) { + BTIF_TRACE_ERROR1("%s ERROR: uinput device open failed", __FUNCTION__); + return -1; + } + memset(&dev, 0, sizeof(dev)); + if (name) + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + + dev.id.bustype = BUS_BLUETOOTH; + dev.id.vendor = 0x0000; + dev.id.product = 0x0000; + dev.id.version = 0x0000; + + if (write(fd, &dev, sizeof(dev)) < 0) { + BTIF_TRACE_ERROR1("%s Unable to write device information", __FUNCTION__); + close(fd); + return -1; + } + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_EVBIT, EV_SYN); + + for (x = 0; key_map[x].name != NULL; x++) + ioctl(fd, UI_SET_KEYBIT, key_map[x].mapped_id); + + for(x = 0; x < KEY_MAX; x++) + ioctl(fd, UI_SET_KEYBIT, x); + + if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { + BTIF_TRACE_ERROR1("%s Unable to create uinput device", __FUNCTION__); + close(fd); + return -1; + } + return fd; +} + +int init_uinput (void) +{ + char *name = "AVRCP"; + + BTIF_TRACE_DEBUG1("%s", __FUNCTION__); + uinput_fd = uinput_create(name); + if (uinput_fd < 0) { + BTIF_TRACE_ERROR3("%s AVRCP: Failed to initialize uinput for %s (%d)", + __FUNCTION__, name, uinput_fd); + } else { + BTIF_TRACE_DEBUG3("%s AVRCP: Initialized uinput for %s (fd=%d)", + __FUNCTION__, name, uinput_fd); + } + return uinput_fd; +} + +void close_uinput (void) +{ + BTIF_TRACE_DEBUG1("%s", __FUNCTION__); + if (uinput_fd > 0) { + ioctl(uinput_fd, UI_DEV_DESTROY); + + close(uinput_fd); + uinput_fd = -1; + } +} +#endif // BTA_AVRCP_FORCE_USE_UINPUT + +const char *dump_rc_event_name(tBTA_AV_EVT event) +{ + switch(event) { + case BTA_AV_RC_OPEN_EVT: return "BTA_AV_RC_OPEN_EVT"; + case BTA_AV_RC_CLOSE_EVT: return "BTA_AV_RC_CLOSE_EVT"; + case BTA_AV_REMOTE_CMD_EVT: return "BTA_AV_REMOTE_CMD_EVT"; + case BTA_AV_REMOTE_RSP_EVT: return "BTA_AV_REMOTE_RSP_EVT"; + case BTA_AV_VENDOR_CMD_EVT: return "BTA_AV_VENDOR_CMD_EVT"; + case BTA_AV_VENDOR_RSP_EVT: return "BTA_AV_VENDOR_RSP_EVT"; + default: return "UNKNOWN_EVENT"; + } +} + +/*************************************************************************** + * Function handle_rc_connect + * + * - Argument: tBTA_AV_RC_OPEN RC open data structure + * + * - Description: RC connection event handler + * + ***************************************************************************/ +void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open) +{ + BTIF_TRACE_DEBUG2("%s: rc_handle: %d", __FUNCTION__, p_rc_open->rc_handle); + +#ifdef BTIF_RC_USE_UINPUT + init_uinput(); +#endif + + memcpy(btif_rc_cb.rc_addr, p_rc_open->peer_addr, sizeof(BD_ADDR)); + btif_rc_cb.rc_connected = TRUE; + btif_rc_cb.rc_handle = p_rc_open->rc_handle; +} + +/*************************************************************************** + * Function handle_rc_disconnect + * + * - Argument: tBTA_AV_RC_CLOSE RC close data structure + * + * - Description: RC disconnection event handler + * + ***************************************************************************/ +void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close) +{ + BTIF_TRACE_DEBUG2("%s: rc_handle: %d", __FUNCTION__, p_rc_close->rc_handle); + + btif_rc_cb.rc_handle = 0; + btif_rc_cb.rc_connected = FALSE; + memset(btif_rc_cb.rc_addr, 0, sizeof(BD_ADDR)); +#ifdef BTIF_RC_USE_UINPUT + close_uinput(); +#endif /* BTIF_RC_USE_UINPUT */ +} + +/*************************************************************************** + * Function handle_rc_passthrough_cmd + * + * - Argument: tBTA_AV_RC rc_id remote control command ID + * tBTA_AV_STATE key_state status of key press + * + * - Description: Remote control command handler + * + ***************************************************************************/ +void handle_rc_passthrough_cmd ( tBTA_AV_REMOTE_CMD *p_remote_cmd) +{ + const char *status; + int pressed, i; + + if (p_remote_cmd->key_state == AVRC_STATE_RELEASE) { + status = "released"; + pressed = 0; + } else { + status = "pressed"; + pressed = 1; + } + + for (i = 0; key_map[i].name != NULL; i++) { + if (p_remote_cmd->rc_id == key_map[i].avrcp) { + BTIF_TRACE_DEBUG3("%s: %s %s", __FUNCTION__, key_map[i].name, status); + + /* MusicPlayer uses a long_press_timeout of 1 second for PLAYPAUSE button + * and maps that to autoshuffle. So if for some reason release for PLAY/PAUSE + * comes 1 second after the press, the MediaPlayer UI goes into a bad state. + * The reason for the delay could be sniff mode exit or some AVDTP procedure etc. + * The fix is to generate a release right after the press and drown the 'actual' + * release. + */ + if ((key_map[i].release_quirk == 1) && (pressed == 0)) + { + BTIF_TRACE_DEBUG2("%s: AVRC %s Release Faked earlier, drowned now", + __FUNCTION__, key_map[i].name); + return; + } +#ifdef BTIF_RC_USE_UINPUT + send_key(uinput_fd, key_map[i].mapped_id, pressed); +#endif + if ((key_map[i].release_quirk == 1) && (pressed == 1)) + { + GKI_delay(30); // 30ms + BTIF_TRACE_DEBUG2("%s: AVRC %s Release quirk enabled, send release now", + __FUNCTION__, key_map[i].name); +#ifdef BTIF_RC_USE_UINPUT + send_key(uinput_fd, key_map[i].mapped_id, 0); +#endif + } + break; + } + } + + if (key_map[i].name == NULL) + BTIF_TRACE_ERROR3("%s AVRCP: unknown button 0x%02X %s", __FUNCTION__, + p_remote_cmd->rc_id, status); +} + +/***************************************************************************** +** +** Function btif_rc_init +** +** Description Initialize RC +** +** Returns Returns 0 on success, -1 otherwise +** +*******************************************************************************/ +int btif_rc_init() +{ + BTIF_TRACE_DEBUG1("%s", __FUNCTION__); + memset (&btif_rc_cb, 0, sizeof(btif_rc_cb)); + +#ifdef BTIF_RC_USE_UINPUT + return uinput_driver_check(); +#endif /* BTIF_RC_USE_UINPUT */ +} + +/*************************************************************************** + ** + ** Function btif_rc_handler + ** + ** Description RC event handler + ** + ***************************************************************************/ +void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) +{ + BTIF_TRACE_DEBUG2 ("%s event:%s", __FUNCTION__, dump_rc_event_name(event)); + switch (event) + { + case BTA_AV_RC_OPEN_EVT: + { + BTIF_TRACE_DEBUG1("Peer_features:%x", p_data->rc_open.peer_features); + handle_rc_connect( &(p_data->rc_open) ); + }break; + + case BTA_AV_RC_CLOSE_EVT: + { + handle_rc_disconnect( &(p_data->rc_close) ); + }break; + + case BTA_AV_REMOTE_CMD_EVT: + { + BTIF_TRACE_DEBUG2("rc_id:0x%x key_state:%d", p_data->remote_cmd.rc_id, + p_data->remote_cmd.key_state); + handle_rc_passthrough_cmd( (&p_data->remote_cmd) ); + }break; + default: + BTIF_TRACE_DEBUG0("Unhandled RC event"); + } +} + +/*************************************************************************** + ** + ** Function btif_rc_get_connected_peer + ** + ** Description Fetches the connected headset's BD_ADDR if any + ** + ***************************************************************************/ +BOOLEAN btif_rc_get_connected_peer(BD_ADDR peer_addr) +{ + if (btif_rc_cb.rc_connected == TRUE) { + bdcpy(peer_addr, btif_rc_cb.rc_addr); + return TRUE; + } + return FALSE; +} |