summaryrefslogtreecommitdiff
path: root/android/avrcp.c
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-03-04 17:34:34 +0200
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-03-05 09:30:40 +0200
commit95f8bca2d32b1bde657ddef5ebdf1b36892df075 (patch)
treeebbb0099615c8a3b4b1ec5249a1c95d5bc762250 /android/avrcp.c
parent6d58ab590116ca5c4690a0d11f81af4a514bb1e4 (diff)
downloadbluez-95f8bca2d32b1bde657ddef5ebdf1b36892df075.tar.gz
android/avrcp: Search for AVRCP CT record before connecting
This is required in order to retrieve version and features which are used by remote features notification.
Diffstat (limited to 'android/avrcp.c')
-rw-r--r--android/avrcp.c207
1 files changed, 154 insertions, 53 deletions
diff --git a/android/avrcp.c b/android/avrcp.c
index 8eac4cd99..319c7e6e7 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -33,7 +33,9 @@
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
#include "src/log.h"
+
#include "avctp.h"
#include "avrcp-lib.h"
#include "hal-msg.h"
@@ -63,6 +65,8 @@ struct avrcp_request {
struct avrcp_device {
bdaddr_t dst;
+ uint16_t version;
+ uint16_t features;
struct avrcp *session;
GIOChannel *io;
GQueue *queue;
@@ -661,12 +665,35 @@ static const struct avrcp_control_handler control_handlers[] = {
{ },
};
-static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+static int avrcp_device_add_session(struct avrcp_device *dev, int fd,
+ uint16_t imtu, uint16_t omtu)
{
- struct avrcp_device *dev;
- bdaddr_t src, dst;
char address[18];
+
+ dev->session = avrcp_new(fd, imtu, omtu, dev->version);
+ if (!dev->session)
+ return -EINVAL;
+
+ avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+ avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+ dev);
+ avrcp_set_control_handlers(dev->session, control_handlers, dev);
+
+ dev->queue = g_queue_new();
+
+ ba2str(&dev->dst, address);
+
+ /* FIXME: get the real name of the device */
+ avrcp_init_uinput(dev->session, "bluetooth", address);
+
+ return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct avrcp_device *dev = user_data;
uint16_t imtu, omtu;
+ char address[18];
GError *gerr = NULL;
int fd;
@@ -676,8 +703,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
}
bt_io_get(chan, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
BT_IO_OPT_IMTU, &imtu,
BT_IO_OPT_OMTU, &omtu,
BT_IO_OPT_INVALID);
@@ -688,37 +714,12 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
return;
}
- ba2str(&dst, address);
-
- dev = avrcp_device_find(&dst);
- if (dev) {
- if (dev->session) {
- error("Unexpected connection");
- return;
- }
- } else {
- DBG("Incoming connection from %s", address);
- dev = avrcp_device_new(&dst);
- }
-
fd = g_io_channel_unix_get_fd(chan);
-
- dev->session = avrcp_new(fd, imtu, omtu, 0x0100);
- if (!dev->session) {
+ if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) {
avrcp_device_free(dev);
return;
}
- avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
- avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
- dev);
- avrcp_set_control_handlers(dev->session, control_handlers, dev);
-
- dev->queue = g_queue_new();
-
- /* FIXME: get the real name of the device */
- avrcp_init_uinput(dev->session, "bluetooth", address);
-
g_io_channel_set_close_on_unref(chan, FALSE);
if (dev->io) {
@@ -729,6 +730,125 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
DBG("%s connected", address);
}
+static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
+{
+ GError *err = NULL;
+
+ dev->io = bt_io_connect(cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return false;
+ }
+
+ return true;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct avrcp_device *dev = data;
+ sdp_list_t *list;
+
+ DBG("");
+
+ if (err < 0) {
+ error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s",
+ strerror(-err));
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("No AVRCP records found");
+ goto fail;
+ }
+
+ for (list = recs; list; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_data_t *data;
+
+ data = sdp_data_get(rec, SDP_ATTR_VERSION);
+ if (data)
+ dev->version = data->val.uint16;
+
+ data = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES);
+ if (data)
+ dev->features = data->val.uint16;
+ }
+
+ if (dev->io) {
+ GError *gerr = NULL;
+ if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) {
+ error("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+ return;
+ }
+
+ if (!avrcp_device_connect(dev, connect_cb)) {
+ error("Unable to connect to AVRCP");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ avrcp_device_remove(dev);
+}
+
+static int avrcp_device_search(struct avrcp_device *dev)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
+
+ return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb,
+ dev, NULL, 0);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct avrcp_device *dev;
+ char address[18];
+ bdaddr_t src, dst;
+ GError *err = NULL;
+
+ bt_io_get(chan, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ DBG("incoming connect from %s", address);
+
+ dev = avrcp_device_find(&dst);
+ if (dev && dev->session) {
+ error("AVRCP: Refusing unexpected connect");
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ dev = avrcp_device_new(&dst);
+ if (avrcp_device_search(dev) < 0) {
+ error("AVRCP: Failed to search SDP details");
+ avrcp_device_free(dev);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ }
+
+ dev->io = g_io_channel_ref(chan);
+}
+
bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
{
GError *err = NULL;
@@ -738,7 +858,7 @@ bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
bacpy(&adapter_addr, addr);
- server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
+ server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
@@ -796,25 +916,6 @@ void bt_avrcp_unregister(void)
}
}
-static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
-{
- GError *err = NULL;
-
- dev->io = bt_io_connect(cb, dev, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- return false;
- }
-
- return true;
-}
-
void bt_avrcp_connect(const bdaddr_t *dst)
{
struct avrcp_device *dev;
@@ -826,9 +927,9 @@ void bt_avrcp_connect(const bdaddr_t *dst)
return;
dev = avrcp_device_new(dst);
- if (!avrcp_device_connect(dev, connect_cb)) {
+ if (avrcp_device_search(dev) < 0) {
+ error("AVRCP: Failed to search SDP details");
avrcp_device_free(dev);
- return;
}
ba2str(&dev->dst, addr);