diff options
Diffstat (limited to 'btif/src/btif_rc.c')
-rw-r--r-- | btif/src/btif_rc.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/btif/src/btif_rc.c b/btif/src/btif_rc.c new file mode 100644 index 0000000..68ddf5b --- /dev/null +++ b/btif/src/btif_rc.c @@ -0,0 +1,492 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + + +/***************************************************************************** + * + * 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 +******************************************************************************/ +#define BTIF_RC_USE_UINPUT TRUE +#include "uinput.h" + +/* cod value for Headsets */ +#define COD_AV_HEADSETS 0x0404 + + +/***************************************************************************** +** 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 +******************************************************************************/ +extern BOOLEAN btif_hf_call_terminated_recently(); +extern BOOLEAN check_cod(const bt_bdaddr_t *remote_bdaddr, uint32_t cod); +extern BOOLEAN btif_av_is_connected(void); + +/***************************************************************************** +** 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; + + btif_rc_cb.rc_handle = p_remote_cmd->rc_handle; + + /* If AVRC is open and peer sends PLAY but there is no AVDT, then we queue-up this PLAY */ + if (p_remote_cmd) + { + /* queue AVRC PLAY if GAVDTP Open notification to app is pending (2 second timer) */ + if ((p_remote_cmd->rc_id == BTA_AV_RC_PLAY) && (!btif_av_is_connected())) + { + if (p_remote_cmd->key_state == AVRC_STATE_PRESS) + { + APPL_TRACE_WARNING1("%s: AVDT not open, queuing the PLAY command", __FUNCTION__); + btif_rc_cb.rc_pending_play = TRUE; + } + return; + } + + if ((p_remote_cmd->rc_id == BTA_AV_RC_PAUSE) && (btif_rc_cb.rc_pending_play)) + { + APPL_TRACE_WARNING1("%s: Clear the pending PLAY on PAUSE received", __FUNCTION__); + btif_rc_cb.rc_pending_play = FALSE; + return; + } + } + if (p_remote_cmd->key_state == AVRC_STATE_RELEASE) { + status = "released"; + pressed = 0; + } else { + status = "pressed"; + pressed = 1; + } + + /* If this is Play/Pause command (press or release) before processing, check the following + * a voice call has ended recently + * the remote device is not of type headset + * If the above conditions meet, drop the Play/Pause command + * This fix is to interop with certain carkits which sends an automatic PLAY or PAUSE + * commands right after call ends + */ + if((p_remote_cmd->rc_id == BTA_AV_RC_PLAY || p_remote_cmd->rc_id == BTA_AV_RC_PAUSE)&& + (btif_hf_call_terminated_recently() == TRUE) && + (check_cod( (const bt_bdaddr_t*)&(btif_rc_cb.rc_addr), COD_AV_HEADSETS) != TRUE)) + { + BTIF_TRACE_DEBUG2("%s:Dropping the play/Pause command received right after call end cmd:%d", + __FUNCTION__,p_remote_cmd->rc_id); + return; + } + + 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; +} + +/*************************************************************************** + ** + ** Function btif_rc_check_handle_pending_play + ** + ** Description Clears the queued PLAY command. if bSend is TRUE, forwards to app + ** + ***************************************************************************/ + +/* clear the queued PLAY command. if bSend is TRUE, forward to app */ +void btif_rc_check_handle_pending_play (BD_ADDR peer_addr, BOOLEAN bSendToApp) +{ + ALOGV("btapp_rc_check_handle_pending_play: bSendToApp=%d", bSendToApp); + if (btif_rc_cb.rc_pending_play) + { + if (bSendToApp) + { + tBTA_AV_REMOTE_CMD remote_cmd; + APPL_TRACE_DEBUG1("%s: Sending queued PLAYED event to app", __FUNCTION__); + + memset (&remote_cmd, 0, sizeof(tBTA_AV_REMOTE_CMD)); + remote_cmd.rc_handle = btif_rc_cb.rc_handle; + remote_cmd.rc_id = AVRC_ID_PLAY; + remote_cmd.hdr.ctype = AVRC_CMD_CTRL; + remote_cmd.hdr.opcode = AVRC_OP_PASS_THRU; + + /* delay sending to app, else there is a timing issue in the framework, + ** which causes the audio to be on th device's speaker. Delay between + ** OPEN & RC_PLAYs + */ + GKI_delay (200); + /* send to app - both PRESSED & RELEASED */ + remote_cmd.key_state = AVRC_STATE_PRESS; + handle_rc_passthrough_cmd( &remote_cmd ); + + GKI_delay (100); + + remote_cmd.key_state = AVRC_STATE_RELEASE; + handle_rc_passthrough_cmd( &remote_cmd ); + } + btif_rc_cb.rc_pending_play = FALSE; + } +} + |