bluez/android/socket.c
Andrzej Kaczmarek 18f045b574 android/socket: Simplify SDP records handling
Records are now created using helper function which creates SDP record
with common contents like service class, name and protocols. Other
attributes are then added by custom functions.
2014-02-12 13:50:49 +01:00

1069 lines
24 KiB
C

/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#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 "src/log.h"
#include "bluetooth.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 channel; /* RFCOMM channel */
/* for socket to BT */
int bt_sock;
guint bt_watch;
/* for socket to HAL */
int jv_sock;
guint jv_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->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len);
if (err < 0) {
error("getsockopt(SO_RCVBUF): %s", strerror(errno));
return -errno;
}
err = getsockopt(rfsock->bt_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 bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock,
rfsock->jv_sock);
if (rfsock->jv_sock >= 0)
if (close(rfsock->jv_sock) < 0)
error("close() fd %d failed: %s", rfsock->jv_sock,
strerror(errno));
if (rfsock->bt_sock >= 0)
if (close(rfsock->bt_sock) < 0)
error("close() fd %d: failed: %s", rfsock->bt_sock,
strerror(errno));
if (rfsock->bt_watch > 0)
if (!g_source_remove(rfsock->bt_watch))
error("bt_watch source was not found");
if (rfsock->jv_watch > 0)
if (!g_source_remove(rfsock->jv_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 bt_sock, int *hal_sock)
{
int fds[2] = {-1, -1};
struct rfcomm_sock *rfsock;
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
error("socketpair(): %s", strerror(errno));
*hal_sock = -1;
return NULL;
}
rfsock = g_new0(struct rfcomm_sock, 1);
rfsock->jv_sock = fds[0];
*hal_sock = fds[1];
rfsock->bt_sock = bt_sock;
DBG("rfsock %p", rfsock);
if (bt_sock < 0)
return rfsock;
if (rfsock_set_buffer(rfsock) < 0) {
cleanup_rfsock(rfsock);
return NULL;
}
return rfsock;
}
static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid,
const char *svc_name,
bool has_obex)
{
sdp_list_t *svclass_id;
sdp_list_t *seq, *proto_seq, *pbg_seq;
sdp_list_t *proto[2];
uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid;
sdp_data_t *channel;
sdp_record_t *record;
record = sdp_record_alloc();
if (!record)
return NULL;
record->handle = sdp_next_handle();
svclass_id = sdp_list_append(NULL, uuid);
sdp_set_service_classes(record, svclass_id);
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
proto[0] = sdp_list_append(NULL, &l2cap_uuid);
seq = 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);
seq = sdp_list_append(seq, proto[1]);
if (has_obex) {
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
proto[2] = sdp_list_append(NULL, &obex_uuid);
seq = sdp_list_append(seq, proto[2]);
}
proto_seq = sdp_list_append(NULL, seq);
sdp_set_access_protos(record, proto_seq);
sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP);
pbg_seq = sdp_list_append(NULL, &pbg_uuid);
sdp_set_browse_groups(record, pbg_seq);
if (svc_name)
sdp_set_info_attr(record, svc_name, NULL, NULL);
sdp_data_free(channel);
sdp_list_free(proto[0], NULL);
sdp_list_free(proto[1], NULL);
if (has_obex)
sdp_list_free(proto[2], NULL);
sdp_list_free(seq, NULL);
sdp_list_free(proto_seq, NULL);
sdp_list_free(pbg_seq, NULL);
sdp_list_free(svclass_id, NULL);
return record;
}
static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
{
uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
uint8_t dtd = SDP_UINT8;
uuid_t uuid;
sdp_list_t *seq;
sdp_profile_desc_t profile[1];
void *dtds[sizeof(formats)], *values[sizeof(formats)];
sdp_data_t *formats_list;
sdp_record_t *record;
size_t i;
sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID);
record = create_rfcomm_record(chan, &uuid, svc_name, true);
if (!record)
return NULL;
sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
profile[0].version = 0x0100;
seq = sdp_list_append(NULL, profile);
sdp_set_profile_descs(record, seq);
for (i = 0; i < sizeof(formats); i++) {
dtds[i] = &dtd;
values[i] = &formats[i];
}
formats_list = sdp_seq_alloc(dtds, values, sizeof(formats));
sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list);
sdp_list_free(seq, NULL);
return record;
}
static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
{
sdp_list_t *seq;
sdp_profile_desc_t profile[1];
uint8_t formats = 0x01;
sdp_record_t *record;
uuid_t uuid;
sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID);
record = create_rfcomm_record(chan, &uuid, svc_name, true);
if (!record)
return NULL;
sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
profile[0].version = 0x0100;
seq = sdp_list_append(NULL, profile);
sdp_set_profile_descs(record, seq);
sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8,
&formats);
sdp_list_free(seq, NULL);
return record;
}
static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
{
sdp_list_t *seq;
sdp_profile_desc_t profile[1];
uint8_t minst = DEFAULT_MAS_INSTANCE;
uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
sdp_record_t *record;
uuid_t uuid;
sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
record = create_rfcomm_record(chan, &uuid, svc_name, true);
if (!record)
return NULL;
sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
profile[0].version = 0x0101;
seq = sdp_list_append(NULL, profile);
sdp_set_profile_descs(record, seq);
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_list_free(seq, NULL);
return record;
}
static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
{
sdp_record_t *record;
uuid_t uuid;
sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID);
record = create_rfcomm_record(chan, &uuid, svc_name, false);
if (!record)
return 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 jv_sock_client_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 %d error", g_io_channel_unix_get_fd(io));
goto fail;
}
len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size);
if (len <= 0) {
error("read(): %s", strerror(errno));
/* Read again */
return TRUE;
}
sent = try_write_all(rfsock->bt_sock, rfsock->buf, len);
if (sent < 0) {
error("write(): %s", strerror(errno));
goto fail;
}
return TRUE;
fail:
DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond);
connections = g_list_remove(connections, rfsock);
cleanup_rfsock(rfsock);
return FALSE;
}
static gboolean bt_sock_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 %d error", g_io_channel_unix_get_fd(io));
goto fail;
}
len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size);
if (len <= 0) {
error("read(): %s", strerror(errno));
/* Read again */
return TRUE;
}
sent = try_write_all(rfsock->jv_sock, rfsock->buf, len);
if (sent < 0) {
error("write(): %s", strerror(errno));
goto fail;
}
return TRUE;
fail:
DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond);
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->jv_sock, &cmd, sizeof(cmd), fd_accepted);
if (len != sizeof(cmd)) {
error("Error sending accept signal");
return false;
}
return true;
}
static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond,
gpointer data)
{
struct rfcomm_sock *rfsock = data;
DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, 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 *new_rfsock;
GIOChannel *jv_io;
GError *gerr = NULL;
bdaddr_t dst;
char address[18];
int new_sock;
int hal_sock;
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 on channel %d (rfsock %p)", address,
rfsock->channel, rfsock);
new_sock = g_io_channel_unix_get_fd(io);
new_rfsock = create_rfsock(new_sock, &hal_sock);
if (!new_rfsock) {
g_io_channel_shutdown(io, TRUE, NULL);
return;
}
DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock,
new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock);
if (!sock_send_accept(rfsock, &dst, hal_sock)) {
cleanup_rfsock(new_rfsock);
return;
}
connections = g_list_append(connections, new_rfsock);
/* Handle events from Android */
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
jv_io = g_io_channel_unix_new(new_rfsock->jv_sock);
id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock);
g_io_channel_unref(jv_io);
new_rfsock->jv_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, bt_sock_event_cb, new_rfsock);
g_io_channel_set_close_on_unref(io, FALSE);
new_rfsock->bt_watch = id;
}
static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid,
uint8_t flags, int *hal_sock)
{
const struct profile_info *profile;
struct rfcomm_sock *rfsock = NULL;
BtIOSecLevel sec_level;
GIOChannel *io, *jv_io;
GIOCondition cond;
GError *err = NULL;
guint id;
uuid_t uu;
char uuid_str[32];
sdp_uuid128_create(&uu, uuid);
sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str,
name);
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("chan %d sec_level %d", chan, sec_level);
rfsock = create_rfsock(-1, hal_sock);
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->bt_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;
jv_io = g_io_channel_unix_new(rfsock->jv_sock);
id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond,
jv_sock_server_event_cb, rfsock,
NULL);
g_io_channel_unref(jv_io);
rfsock->jv_watch = id;
DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock,
rfsock->bt_sock,
rfsock->jv_sock,
*hal_sock);
if (write(rfsock->jv_sock, &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_sock);
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_sock;
switch (cmd->type) {
case HAL_SOCK_RFCOMM:
status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid,
cmd->flags, &hal_sock);
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_sock);
close(hal_sock);
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->jv_sock, &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 *jv_io;
char address[18];
guint id;
GIOCondition cond;
if (err) {
error("%s", err->message);
goto fail;
}
ba2str(dst, address);
DBG("Connected to %s on channel %d (rfsock %p)", address,
rfsock->channel, rfsock);
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;
jv_io = g_io_channel_unix_new(rfsock->jv_sock);
id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock);
g_io_channel_unref(jv_io);
rfsock->jv_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, bt_sock_event_cb, rfsock);
g_io_channel_set_close_on_unref(io, FALSE);
rfsock->bt_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;
DBG("rfsock %p sec_level %d chan %d", rfsock, sec_level, chan);
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->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) {
error("Error sending RFCOMM channel");
return false;
}
rfsock->bt_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_sock)
{
struct rfcomm_sock *rfsock;
char address[18];
uuid_t uu;
char uuid_str[32];
sdp_uuid128_create(&uu, uuid);
sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
ba2str(addr, address);
DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags,
uuid_str);
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_sock);
if (!rfsock)
return HAL_STATUS_FAILED;
DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock,
*hal_sock);
bacpy(&rfsock->dst, addr);
if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) {
if (!do_rfcomm_connect(rfsock, chan))
goto failed;
} else {
rfsock->profile = get_profile_by_uuid(uuid);
if (bt_search_service(&adapter_addr, &rfsock->dst, &uu,
sdp_search_cb, rfsock, NULL, 0) < 0) {
error("Failed to search SDP records");
goto failed;
}
}
return HAL_STATUS_SUCCESS;
failed:
cleanup_rfsock(rfsock);
close(*hal_sock);
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_sock;
DBG("");
android2bdaddr(cmd->bdaddr, &bdaddr);
switch (cmd->type) {
case HAL_SOCK_RFCOMM:
status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid,
cmd->flags, &hal_sock);
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_sock);
close(hal_sock);
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);
}