/****************************************************************************** * * 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 #include #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; } }