/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "lib/bluetooth.h" #include "btio/btio.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/sdpd.h" #include "bluetooth.h" #include "log.h" #include "hal-msg.h" #include "hal-ipc.h" #include "ipc.h" #include "utils.h" #include "socket.h" #define SPP_DEFAULT_CHANNEL 3 #define OPP_DEFAULT_CHANNEL 9 #define PBAP_DEFAULT_CHANNEL 15 #define MAS_DEFAULT_CHANNEL 16 #define SVC_HINT_OBEX 0x10 /* Hardcoded MAP stuff needed for MAS SMS Instance.*/ #define DEFAULT_MAS_INSTANCE 0x00 #define MAP_MSG_TYPE_SMS_GSM 0x02 #define MAP_MSG_TYPE_SMS_CDMA 0x04 #define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA) static bdaddr_t adapter_addr; static const uint8_t zero_uuid[16] = { 0 }; /* Simple list of RFCOMM server sockets */ GList *servers = NULL; /* Simple list of RFCOMM connected sockets */ GList *connections = NULL; struct rfcomm_sock { int fd; /* descriptor for communication with Java framework */ int real_sock; /* real RFCOMM socket */ int channel; /* RFCOMM channel */ guint rfcomm_watch; guint stack_watch; bdaddr_t dst; uint32_t service_handle; uint8_t *buf; int buf_size; const struct profile_info *profile; }; static int rfsock_set_buffer(struct rfcomm_sock *rfsock) { socklen_t len = sizeof(int); int rcv, snd, size, err; err = getsockopt(rfsock->real_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len); if (err < 0) { error("getsockopt(SO_RCVBUF): %s", strerror(errno)); return -errno; } err = getsockopt(rfsock->real_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len); if (err < 0) { error("getsockopt(SO_SNDBUF): %s", strerror(errno)); return -errno; } size = MAX(rcv, snd); DBG("Set buffer size %d", size); rfsock->buf = g_malloc(size); rfsock->buf_size = size; return 0; } static void cleanup_rfsock(gpointer data) { struct rfcomm_sock *rfsock = data; DBG("rfsock: %p fd %d real_sock %d chan %u", rfsock, rfsock->fd, rfsock->real_sock, rfsock->channel); if (rfsock->fd >= 0) if (close(rfsock->fd) < 0) error("close() fd %d failed: %s", rfsock->fd, strerror(errno)); if (rfsock->real_sock >= 0) if (close(rfsock->real_sock) < 0) error("close() fd %d: failed: %s", rfsock->real_sock, strerror(errno)); if (rfsock->rfcomm_watch > 0) if (!g_source_remove(rfsock->rfcomm_watch)) error("rfcomm_watch source was not found"); if (rfsock->stack_watch > 0) if (!g_source_remove(rfsock->stack_watch)) error("stack_watch source was not found"); if (rfsock->service_handle) bt_adapter_remove_record(rfsock->service_handle); if (rfsock->buf) g_free(rfsock->buf); g_free(rfsock); } static struct rfcomm_sock *create_rfsock(int sock, int *hal_fd) { int fds[2] = {-1, -1}; struct rfcomm_sock *rfsock; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { error("socketpair(): %s", strerror(errno)); *hal_fd = -1; return NULL; } rfsock = g_new0(struct rfcomm_sock, 1); rfsock->fd = fds[0]; *hal_fd = fds[1]; rfsock->real_sock = sock; if (sock < 0) return rfsock; if (rfsock_set_buffer(rfsock) < 0) { cleanup_rfsock(rfsock); return NULL; } return rfsock; } static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) { const char *service_name = "OBEX Object Push"; sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[3]; uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff }; void *dtds[sizeof(formats)], *values[sizeof(formats)]; unsigned int i; uint8_t dtd = SDP_UINT8; sdp_data_t *sflist; sdp_data_t *channel; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->handle = sdp_next_handle(); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &opush_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); sdp_uuid16_create(&obex_uuid, OBEX_UUID); proto[2] = sdp_list_append(NULL, &obex_uuid); apseq = sdp_list_append(apseq, proto[2]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); for (i = 0; i < sizeof(formats); i++) { dtds[i] = &dtd; values[i] = &formats[i]; } sflist = sdp_seq_alloc(dtds, values, sizeof(formats)); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist); if (svc_name) service_name = svc_name; sdp_set_info_attr(record, service_name, NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(proto[2], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) { const char *service_name = "OBEX Phonebook Access Server"; sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[3]; sdp_data_t *channel; uint8_t formats[] = { 0x01 }; uint8_t dtd = SDP_UINT8; sdp_data_t *sflist; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->handle = sdp_next_handle(); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &pbap_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); sdp_uuid16_create(&obex_uuid, OBEX_UUID); proto[2] = sdp_list_append(NULL, &obex_uuid); apseq = sdp_list_append(apseq, proto[2]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sflist = sdp_data_alloc(dtd, formats); sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist); if (svc_name) service_name = svc_name; sdp_set_info_attr(record, service_name, NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(proto[2], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name) { const char *service_name = "MAP MAS SMS"; sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, mse_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[3]; sdp_data_t *channel; uint8_t minst = DEFAULT_MAS_INSTANCE; uint8_t mtype = DEFAULT_MAS_MSG_TYPE; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->handle = sdp_next_handle(); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&mse_uuid, MAP_MSE_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &mse_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID); profile[0].version = 0x0101; pfseq = sdp_list_append(NULL, profile); sdp_set_profile_descs(record, pfseq); sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst); sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8, &mtype); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); sdp_uuid16_create(&obex_uuid, OBEX_UUID); proto[2] = sdp_list_append(NULL, &obex_uuid); apseq = sdp_list_append(apseq, proto[2]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); if (svc_name) service_name = svc_name; sdp_set_info_attr(record, service_name, NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(proto[2], NULL); sdp_list_free(apseq, NULL); sdp_list_free(pfseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); return record; } static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) { const char *service_name = "Serial Port"; sdp_list_t *svclass_id, *apseq, *profiles, *root; uuid_t root_uuid, sp_uuid, l2cap, rfcomm; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_data_t *channel; sdp_record_t *record; record = sdp_record_alloc(); if (!record) return NULL; record->handle = sdp_next_handle(); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID); svclass_id = sdp_list_append(NULL, &sp_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); profile.version = 0x0100; profiles = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, profiles); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); sdp_add_lang_attr(record); if (svc_name) service_name = svc_name; sdp_set_info_attr(record, service_name, "BlueZ", "COM Port"); sdp_set_url_attr(record, "http://www.bluez.org/", "http://www.bluez.org/", "http://www.bluez.org/"); sdp_set_service_id(record, sp_uuid); sdp_set_service_ttl(record, 0xffff); sdp_set_service_avail(record, 0xff); sdp_set_record_state(record, 0x00001234); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); sdp_list_free(root, NULL); sdp_list_free(svclass_id, NULL); sdp_list_free(profiles, NULL); return record; } static const struct profile_info { uint8_t uuid[16]; uint8_t channel; uint8_t svc_hint; BtIOSecLevel sec_level; sdp_record_t * (*create_record)(uint8_t chan, const char *svc_name); } profiles[] = { { .uuid = { 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = PBAP_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_pbap_record }, { .uuid = { 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = OPP_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_LOW, .create_record = create_opp_record }, { .uuid = { 0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = MAS_DEFAULT_CHANNEL, .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_mas_record }, { .uuid = { 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, .channel = SPP_DEFAULT_CHANNEL, .svc_hint = 0, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_spp_record }, }; static uint32_t sdp_service_register(const struct profile_info *profile, const void *svc_name) { sdp_record_t *record; if (!profile || !profile->create_record) return 0; record = profile->create_record(profile->channel, svc_name); if (!record) return 0; if (bt_adapter_add_record(record, profile->svc_hint) < 0) { error("Failed to register on SDP record"); sdp_record_free(record); return 0; } return record->handle; } static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd) { ssize_t ret; struct msghdr msg; struct cmsghdr *cmsg; struct iovec iv; char cmsgbuf[CMSG_SPACE(sizeof(int))]; DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd); if (sock_fd == -1 || send_fd == -1) return -1; memset(&msg, 0, sizeof(msg)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); iv.iov_base = (unsigned char *) buf; iv.iov_len = len; msg.msg_iov = &iv; msg.msg_iovlen = 1; ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL); if (ret < 0) { error("sendmsg(): sock_fd %d send_fd %d: %s", sock_fd, send_fd, strerror(errno)); return ret; } return ret; } static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid) { unsigned int i; for (i = 0; i < G_N_ELEMENTS(profiles); i++) { if (!memcmp(profiles[i].uuid, uuid, 16)) return &profiles[i]; } return NULL; } static int try_write_all(int fd, unsigned char *buf, int len) { int sent = 0; while (len > 0) { int written; written = write(fd, buf, len); if (written < 0) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (!written) return 0; len -= written; buf += written; sent += written; } return sent; } static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; int len, sent; if (cond & G_IO_HUP) { DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); goto fail; } if (cond & (G_IO_ERR | G_IO_NVAL)) { error("Socket error: sock %d cond %d", g_io_channel_unix_get_fd(io), cond); goto fail; } len = read(rfsock->fd, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } sent = try_write_all(rfsock->real_sock, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; } return TRUE; fail: connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); return FALSE; } static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; int len, sent; if (cond & G_IO_HUP) { DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); goto fail; } if (cond & (G_IO_ERR | G_IO_NVAL)) { error("Socket error: sock %d cond %d", g_io_channel_unix_get_fd(io), cond); goto fail; } len = read(rfsock->real_sock, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } sent = try_write_all(rfsock->fd, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; } return TRUE; fail: connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); return FALSE; } static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, int fd_accepted) { struct hal_sock_connect_signal cmd; int len; DBG(""); cmd.size = sizeof(cmd); bdaddr2android(bdaddr, cmd.bdaddr); cmd.channel = rfsock->channel; cmd.status = 0; len = bt_sock_send_fd(rfsock->fd, &cmd, sizeof(cmd), fd_accepted); if (len != sizeof(cmd)) { error("Error sending accept signal"); return false; } return true; } static gboolean sock_server_stack_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; DBG("sock %d cond %d", g_io_channel_unix_get_fd(io), cond); if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_ERR | G_IO_HUP )) { servers = g_list_remove(servers, rfsock); cleanup_rfsock(rfsock); } return FALSE; } static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; struct rfcomm_sock *rfsock_acc; GIOChannel *io_stack; GError *gerr = NULL; bdaddr_t dst; char address[18]; int sock_acc; int hal_fd; guint id; GIOCondition cond; if (err) { error("%s", err->message); return; } bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); g_io_channel_shutdown(io, TRUE, NULL); return; } ba2str(&dst, address); DBG("Incoming connection from %s rfsock %p", address, rfsock); sock_acc = g_io_channel_unix_get_fd(io); rfsock_acc = create_rfsock(sock_acc, &hal_fd); if (!rfsock_acc) { g_io_channel_shutdown(io, TRUE, NULL); return; } DBG("rfsock: fd %d real_sock %d chan %u sock %d", rfsock->fd, rfsock->real_sock, rfsock->channel, sock_acc); if (!sock_send_accept(rfsock, &dst, hal_fd)) { cleanup_rfsock(rfsock_acc); return; } connections = g_list_append(connections, rfsock_acc); /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; io_stack = g_io_channel_unix_new(rfsock_acc->fd); id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock_acc); g_io_channel_unref(io_stack); rfsock_acc->stack_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock_acc); g_io_channel_set_close_on_unref(io, FALSE); rfsock_acc->rfcomm_watch = id; DBG("rfsock %p rfsock_acc %p stack_watch %d rfcomm_watch %d", rfsock, rfsock_acc, rfsock_acc->stack_watch, rfsock_acc->rfcomm_watch); } static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid, uint8_t flags, int *hal_fd) { const struct profile_info *profile; struct rfcomm_sock *rfsock = NULL; BtIOSecLevel sec_level; GIOChannel *io, *io_stack; GIOCondition cond; GError *err = NULL; guint id; DBG(""); if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) { error("Invalid rfcomm listen params"); return HAL_STATUS_INVALID; } profile = get_profile_by_uuid(uuid); if (!profile) { if (chan <= 0) return HAL_STATUS_INVALID; sec_level = BT_IO_SEC_MEDIUM; } else { chan = profile->channel; sec_level = profile->sec_level; } DBG("rfcomm channel %d svc_name %s", chan, name); rfsock = create_rfsock(-1, hal_fd); if (!rfsock) return HAL_STATUS_FAILED; io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!io) { error("Failed listen: %s", err->message); g_error_free(err); goto failed; } rfsock->real_sock = g_io_channel_unix_get_fd(io); g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(io); /* Handle events from Android */ cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; io_stack = g_io_channel_unix_new(rfsock->fd); id = g_io_add_watch_full(io_stack, G_PRIORITY_HIGH, cond, sock_server_stack_event_cb, rfsock, NULL); g_io_channel_unref(io_stack); rfsock->stack_watch = id; DBG("real_sock %d fd %d hal_fd %d", rfsock->real_sock, rfsock->fd, *hal_fd); if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { error("Error sending RFCOMM channel"); goto failed; } rfsock->service_handle = sdp_service_register(profile, name); servers = g_list_append(servers, rfsock); return HAL_STATUS_SUCCESS; failed: cleanup_rfsock(rfsock); close(*hal_fd); return HAL_STATUS_FAILED; } static void handle_listen(const void *buf, uint16_t len) { const struct hal_cmd_sock_listen *cmd = buf; uint8_t status; int hal_fd; switch (cmd->type) { case HAL_SOCK_RFCOMM: status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid, cmd->flags, &hal_fd); break; case HAL_SOCK_SCO: case HAL_SOCK_L2CAP: status = HAL_STATUS_UNSUPPORTED; break; default: status = HAL_STATUS_INVALID; break; } if (status != HAL_STATUS_SUCCESS) goto failed; ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, 0, NULL, hal_fd); close(hal_fd); return ; failed: ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, status); } static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) { struct hal_sock_connect_signal cmd; int len; DBG(""); memset(&cmd, 0, sizeof(cmd)); cmd.size = sizeof(cmd); bdaddr2android(bdaddr, cmd.bdaddr); cmd.channel = rfsock->channel; cmd.status = 0; len = write(rfsock->fd, &cmd, sizeof(cmd)); if (len < 0) { error("%s", strerror(errno)); return false; } if (len != sizeof(cmd)) { error("Error sending connect signal"); return false; } return true; } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; bdaddr_t *dst = &rfsock->dst; GIOChannel *io_stack; char address[18]; guint id; GIOCondition cond; if (err) { error("%s", err->message); goto fail; } ba2str(dst, address); DBG("Connected to %s", address); DBG("rfsock: fd %d real_sock %d chan %u sock %d", rfsock->fd, rfsock->real_sock, rfsock->channel, g_io_channel_unix_get_fd(io)); if (!sock_send_connect(rfsock, dst)) goto fail; /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; io_stack = g_io_channel_unix_new(rfsock->fd); id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock); g_io_channel_unref(io_stack); rfsock->stack_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock); g_io_channel_set_close_on_unref(io, FALSE); rfsock->rfcomm_watch = id; return; fail: connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); } static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan) { BtIOSecLevel sec_level = BT_IO_SEC_MEDIUM; GIOChannel *io; GError *gerr = NULL; if (rfsock->profile) sec_level = rfsock->profile->sec_level; io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &rfsock->dst, BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (!io) { error("Failed connect: %s", gerr->message); g_error_free(gerr); return false; } g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(io); if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { error("Error sending RFCOMM channel"); return false; } rfsock->real_sock = g_io_channel_unix_get_fd(io); rfsock_set_buffer(rfsock); rfsock->channel = chan; connections = g_list_append(connections, rfsock); return true; } static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct rfcomm_sock *rfsock = data; sdp_list_t *list; int chan; DBG(""); if (err < 0) { error("Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("No SDP records found"); goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_list_t *protos; if (sdp_get_access_protos(rec, &protos) < 0) { error("Unable to get proto list"); goto fail; } chan = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); if (chan) break; } if (chan <= 0) { error("Could not get RFCOMM channel %d", chan); goto fail; } DBG("Got RFCOMM channel %d", chan); if (do_rfcomm_connect(rfsock, chan)) return; fail: cleanup_rfsock(rfsock); } static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan, const uint8_t *uuid, uint8_t flags, int *hal_fd) { struct rfcomm_sock *rfsock; uuid_t uu; if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || !bacmp(addr, BDADDR_ANY)) { error("Invalid rfcomm connect params"); return HAL_STATUS_INVALID; } rfsock = create_rfsock(-1, hal_fd); if (!rfsock) return HAL_STATUS_FAILED; bacpy(&rfsock->dst, addr); if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) { if (!do_rfcomm_connect(rfsock, chan)) goto failed; } else { memset(&uu, 0, sizeof(uu)); uu.type = SDP_UUID128; memcpy(&uu.value.uuid128, uuid, sizeof(uint128_t)); rfsock->profile = get_profile_by_uuid(uuid); if (bt_search_service(&adapter_addr, &rfsock->dst, &uu, sdp_search_cb, rfsock, NULL) < 0) { error("Failed to search SDP records"); goto failed; } } return HAL_STATUS_SUCCESS; failed: cleanup_rfsock(rfsock); close(*hal_fd); return HAL_STATUS_FAILED; } static void handle_connect(const void *buf, uint16_t len) { const struct hal_cmd_sock_connect *cmd = buf; bdaddr_t bdaddr; uint8_t status; int hal_fd; DBG(""); android2bdaddr(cmd->bdaddr, &bdaddr); switch (cmd->type) { case HAL_SOCK_RFCOMM: status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid, cmd->flags, &hal_fd); break; case HAL_SOCK_SCO: case HAL_SOCK_L2CAP: status = HAL_STATUS_UNSUPPORTED; break; default: status = HAL_STATUS_INVALID; break; } if (status != HAL_STATUS_SUCCESS) goto failed; ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, 0, NULL, hal_fd); close(hal_fd); return; failed: ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, status); } static const struct ipc_handler cmd_handlers[] = { /* HAL_OP_SOCK_LISTEN */ { handle_listen, false, sizeof(struct hal_cmd_sock_listen) }, /* HAL_OP_SOCK_CONNECT */ { handle_connect, false, sizeof(struct hal_cmd_sock_connect) }, }; void bt_socket_register(const bdaddr_t *addr) { DBG(""); bacpy(&adapter_addr, addr); ipc_register(HAL_SERVICE_ID_SOCK, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); } void bt_socket_unregister(void) { DBG(""); g_list_free_full(connections, cleanup_rfsock); g_list_free_full(servers, cleanup_rfsock); ipc_unregister(HAL_SERVICE_ID_SOCK); }