summaryrefslogtreecommitdiffstats
path: root/btif/src/btif_rc.c
diff options
context:
space:
mode:
Diffstat (limited to 'btif/src/btif_rc.c')
-rw-r--r--btif/src/btif_rc.c492
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;
+ }
+}
+